bitkeeper revision 1.1010.1.11 (40e03333GhMB8qgYk92TiquiQmUajA)
authormjw@wray-m-3.hpl.hp.com <mjw@wray-m-3.hpl.hp.com>
Mon, 28 Jun 2004 15:03:15 +0000 (15:03 +0000)
committermjw@wray-m-3.hpl.hp.com <mjw@wray-m-3.hpl.hp.com>
Mon, 28 Jun 2004 15:03:15 +0000 (15:03 +0000)
Change domain save/restore deal with the config string.

55 files changed:
.rootkeys
BitKeeper/etc/ignore
tools/lib/allocate.c [new file with mode: 0644]
tools/lib/allocate.h [new file with mode: 0644]
tools/lib/debug.h [new file with mode: 0644]
tools/lib/enum.c [new file with mode: 0644]
tools/lib/enum.h [new file with mode: 0644]
tools/lib/file_stream.c [new file with mode: 0644]
tools/lib/file_stream.h [new file with mode: 0644]
tools/lib/gzip_stream.c [new file with mode: 0644]
tools/lib/gzip_stream.h [new file with mode: 0644]
tools/lib/hash_table.c [new file with mode: 0644]
tools/lib/hash_table.h [new file with mode: 0644]
tools/lib/iostream.c [new file with mode: 0644]
tools/lib/iostream.h [new file with mode: 0644]
tools/lib/kernel_stream.c [new file with mode: 0644]
tools/lib/kernel_stream.h [new file with mode: 0644]
tools/lib/lexis.c [new file with mode: 0644]
tools/lib/lexis.h [new file with mode: 0644]
tools/lib/lzi_stream.c [new file with mode: 0644]
tools/lib/lzi_stream.h [new file with mode: 0644]
tools/lib/lzo_stream.c [new file with mode: 0644]
tools/lib/lzo_stream.h [new file with mode: 0644]
tools/lib/marshal.c [new file with mode: 0644]
tools/lib/marshal.h [new file with mode: 0644]
tools/lib/socket_stream.c [new file with mode: 0644]
tools/lib/socket_stream.h [new file with mode: 0644]
tools/lib/string_stream.c [new file with mode: 0644]
tools/lib/string_stream.h [new file with mode: 0644]
tools/lib/sxpr.c [new file with mode: 0644]
tools/lib/sxpr.h [new file with mode: 0644]
tools/lib/sxpr_parser.c [new file with mode: 0644]
tools/lib/sxpr_parser.h [new file with mode: 0644]
tools/lib/sys_ctype.h [new file with mode: 0644]
tools/lib/sys_net.c [new file with mode: 0644]
tools/lib/sys_net.h [new file with mode: 0644]
tools/lib/sys_string.c [new file with mode: 0644]
tools/lib/sys_string.h [new file with mode: 0644]
tools/lib/xdr.c [new file with mode: 0644]
tools/lib/xdr.h [new file with mode: 0644]
tools/xc/lib/Makefile
tools/xc/lib/xc.h
tools/xc/lib/xc_io.c [new file with mode: 0644]
tools/xc/lib/xc_io.h [new file with mode: 0644]
tools/xc/lib/xc_linux_restore.c
tools/xc/lib/xc_linux_save.c
tools/xc/lib/xc_private.h
tools/xc/py/Xc.c
tools/xc/py/setup.py
tools/xen/lib/xend/XendDomain.py
tools/xen/lib/xend/XendDomainInfo.py
tools/xen/lib/xend/server/SrvDomain.py
tools/xen/lib/xend/server/SrvDomainDir.py
tools/xen/lib/xend/sxp.py
tools/xen/xend

index 8418e7c6689930f7f6524d6f4daaca9715039f09..adc1e74bcd48b01779705afec8ac65ecea858af0 100644 (file)
--- a/.rootkeys
+++ b/.rootkeys
 40c9c468pXANclL7slGaoD0kSrIwoQ tools/examples/xm_dom_create.py
 40cf2937oKlROYOJTN8GWwWM5AmjBg tools/examples/xmdefaults
 40dfd40auJwNnb8NoiSnRkvZaaXkUg tools/examples/xmnetbsd
+40e033325Sjqs-_4TuzeUEprP_gYFg tools/lib/allocate.c
+40e03332KYz7o1bn2MG_KPbBlyoIMA tools/lib/allocate.h
+40e03332IyRttYoXKoJla5qCC514SQ tools/lib/debug.h
+40e03332qV5tJ-GJZjo-LBCeGuEjJA tools/lib/enum.c
+40e03332wwMVxfobgA1PSMTSAGLiCw tools/lib/enum.h
+40e03332p5Dc_owJQRuN72ymJZddFQ tools/lib/file_stream.c
+40e03332jWfB2viAhLSkq1WK0r_iDQ tools/lib/file_stream.h
+40e03332rUjNMGg11n2rN6V4DCrvOg tools/lib/gzip_stream.c
+40e033321O5Qg22haLoq5lpmk4tooQ tools/lib/gzip_stream.h
+40e03332QrTR96tc6yS2rMBpd2mq1A tools/lib/hash_table.c
+40e033325KoIb0d_uy8s7b5DUR9fPQ tools/lib/hash_table.h
+40e03332ihnBGzHykVwZnFmkAppb4g tools/lib/iostream.c
+40e03332UGwbLR4wsw4ft14p0Yw5pg tools/lib/iostream.h
+40e0333245DLDzJemeSVBLuutHtzEQ tools/lib/kernel_stream.c
+40e03332aK0GkgpDdc-PVTkWKTeOBg tools/lib/kernel_stream.h
+40e03332HJ0cDcZDKDUUT-tEiBWOZw tools/lib/lexis.c
+40e03332tnH9Ggzxbfi3xY9Vh2hUlg tools/lib/lexis.h
+40e03332aYIW0BNBh6wXuKKn_P7Yyg tools/lib/lzi_stream.c
+40e0333233voTffE4cJSMGJARfiSSQ tools/lib/lzi_stream.h
+40e03332FXuMoUnfsAKSgV8X4rFbYQ tools/lib/lzo_stream.c
+40e03332InJaiLfpDcIXBy2fI0RFGQ tools/lib/lzo_stream.h
+40e03332a5SCuRsejHStTuWzMQNv8Q tools/lib/marshal.c
+40e03332TwKyJrZQiiQfNq4vc2hpgw tools/lib/marshal.h
+40e033328ccHlJuTR1FswYL_EC6LFA tools/lib/socket_stream.c
+40e03332P0KVQGkmahj47aafo1X0nA tools/lib/socket_stream.h
+40e03332KT_tnnoAMbPVAZBB7kSOAQ tools/lib/string_stream.c
+40e03332-VtK6_OZa1vMHXFil8uq6w tools/lib/string_stream.h
+40e03332dDtczi6YX7_mMxhYjJeAdQ tools/lib/sxpr.c
+40e03332QPuyNKDOTIYVvkwK5qO-vg tools/lib/sxpr.h
+40e03332Pi0_osJ3XPBi38ADPqdl4A tools/lib/sxpr_parser.c
+40e033324v5QFMvWEXXzv38uUT9kHg tools/lib/sxpr_parser.h
+40e03332gKUInsqtxQOV4mPiMqf_dg tools/lib/sys_ctype.h
+40e03332Rkvq6nn_UNjzAAK_Tk9v1g tools/lib/sys_net.c
+40e03332lQHvQHw4Rh7VsT1_sui29A tools/lib/sys_net.h
+40e033321smklZd7bDSdWvQCeIshtg tools/lib/sys_string.c
+40e03332h5V611rRWURRLqb1Ekatxg tools/lib/sys_string.h
+40e03332u4q5kgF0N7RfqB4s0pZVew tools/lib/xdr.c
+40e03332hY16nfRXF4gGd5S1aUJUBw tools/lib/xdr.h
 3f776bd2Xd-dUcPKlPN2vG89VGtfvQ tools/misc/Makefile
 40ab2cfawIw8tsYo0dQKtp83h4qfTQ tools/misc/fakei386xen
 3f6dc136ZKOjd8PIqLbFBl_v-rnkGg tools/misc/miniterm/Makefile
 3fbba6dbasJQV-MVElDC0DGSHMiL5w tools/xc/lib/xc_domain.c
 40278d99BLsfUv3qxv0I8C1sClZ0ow tools/xc/lib/xc_elf.h
 403e0977Bjsm_e82pwvl9VvaJxh8Gg tools/xc/lib/xc_evtchn.c
+40e03333Eegw8czSWvHsbKxrRZJjRA tools/xc/lib/xc_io.c
+40e03333vrWGbLAhyJjXlqCHaJt7eA tools/xc/lib/xc_io.h
 3fbba6dbNCU7U6nsMYiXzKkp3ztaJg tools/xc/lib/xc_linux_build.c
 3fbba6dbl267zZOAVHYLOdLCdhcZMw tools/xc/lib/xc_linux_restore.c
 3fbba6db7li3FJiABYtCmuGxOJxEGw tools/xc/lib/xc_linux_save.c
index 51b5f03492f30bbf5fa400ecd2521cf964b92723..5db69fd7ef1f49835b7d653563c57ceca6688444 100644 (file)
@@ -31,3 +31,21 @@ xen/tools/figlet/figlet
 xen/xen
 xen/xen-syms
 xen/xen.*
+tools/xc/lib/.allocate.o.d
+tools/xc/lib/.file_stream.o.d
+tools/xc/lib/.gzip_stream.o.d
+tools/xc/lib/.iostream.o.d
+tools/xc/lib/.sys_net.o.d
+tools/xc/lib/.sys_string.o.d
+tools/xc/lib/.xc_atropos.o.d
+tools/xc/lib/.xc_bvtsched.o.d
+tools/xc/lib/.xc_domain.o.d
+tools/xc/lib/.xc_evtchn.o.d
+tools/xc/lib/.xc_io.o.d
+tools/xc/lib/.xc_linux_build.o.d
+tools/xc/lib/.xc_linux_restore.o.d
+tools/xc/lib/.xc_linux_save.o.d
+tools/xc/lib/.xc_misc.o.d
+tools/xc/lib/.xc_netbsd_build.o.d
+tools/xc/lib/.xc_physdev.o.d
+tools/xc/lib/.xc_private.o.d
diff --git a/tools/lib/allocate.c b/tools/lib/allocate.c
new file mode 100644 (file)
index 0000000..600ebab
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2001 - 2004 Mike Wray <mike.wray@hp.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "allocate.h"
+
+/** @file
+ * Support for allocating memory.
+ * Usable from user code or kernel code (with __KERNEL__ defined).
+ * In user code will use GC if USE_GC is defined.
+ */
+
+#ifdef __KERNEL__
+/*----------------------------------------------------------------------------*/
+#  include <linux/config.h>
+#  include <linux/slab.h>
+#  include <linux/string.h>
+#  include <linux/types.h>
+
+#  define DEFAULT_TYPE    0
+#  define MALLOC(n, type) kmalloc(n, type)
+#  define FREE(ptr)       kfree(ptr)
+
+/*----------------------------------------------------------------------------*/
+#else /* ! __KERNEL__ */
+
+#  include <stdlib.h>
+#  include <string.h>
+
+#  define DEFAULT_TYPE    0
+
+#ifdef USE_GC
+#  include "gc.h"
+#  define MALLOC(n, typ)  GC_malloc(n)
+#  define FREE(ptr)       (ptr=NULL)
+//typedef void *GC_PTR;
+//GC_PTR (*GC_oom_fn)(size_t n);
+#else
+#  define MALLOC(n, type) malloc(n)
+#  define FREE(ptr)       free(ptr)
+#endif
+
+/*----------------------------------------------------------------------------*/
+#endif
+
+/** Function to call when memory cannot be allocated. */
+AllocateFailedFn *allocate_failed_fn = NULL;
+
+/** Allocate memory and zero it.
+ * The type is only relevant when calling from kernel code,
+ * from user code it is ignored.
+ * In kernel code the values accepted by kmalloc can be used:
+ * GFP_USER, GFP_ATOMIC, GFP_KERNEL.
+ *
+ * @param size number of bytes to allocate
+ * @param type memory type to allocate (kernel only)
+ * @return pointer to the allocated memory or zero
+ * if malloc failed
+ */
+void *allocate_type(int size, int type){
+    void *p = MALLOC(size, type);
+    if(p){
+        memzero(p, size);
+    } else if(allocate_failed_fn){
+        allocate_failed_fn(size, type);
+    }
+    return p;
+}
+
+/** Allocate memory and zero it.
+ *
+ * @param size number of bytes to allocate
+ * @return pointer to the allocated memory or zero
+ * if malloc failed
+ */
+void *allocate(int size){
+    return allocate_type(size, DEFAULT_TYPE);
+}
+
+/** Free memory allocated by allocate().
+ * No-op if 'p' is null.
+ *
+ * @param p memory to free
+ */
+void deallocate(void *p){
+    if(p){
+        FREE(p);
+    }
+}
+
+/** Set bytes to zero.
+ * No-op if 'p' is null.
+ *
+ * @param p memory to zero
+ * @param size number of bytes to zero
+ */
+void memzero(void *p, int size){
+    if(p){
+        memset(p, 0, (size_t)size);
+    }
+}
+
diff --git a/tools/lib/allocate.h b/tools/lib/allocate.h
new file mode 100644 (file)
index 0000000..08bc67b
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2001 - 2004 Mike Wray <mike.wray@hp.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef _XEN_LIB_ALLOCATE_H_
+#define _XEN_LIB_ALLOCATE_H_
+
+/** Allocate memory for a given type, and cast. */
+#define ALLOCATE(ctype) (ctype *)allocate(sizeof(ctype))
+
+/** Allocate memory for a given type, and cast. */
+#define ALLOCATE_TYPE(ctype, type) (ctype *)allocate(sizeof(ctype))
+
+extern void *allocate_type(int size, int type);
+extern void *allocate(int size);
+extern void deallocate(void *);
+extern void memzero(void *p, int size);
+
+typedef void AllocateFailedFn(int size, int type);
+extern AllocateFailedFn *allocate_failed_fn;
+
+#endif /* _XEN_LIB_ALLOCATE_H_ */
+
+
+
+
+
+
+
+
+
diff --git a/tools/lib/debug.h b/tools/lib/debug.h
new file mode 100644 (file)
index 0000000..4f5228f
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef _XEN_LIB_DEBUG_H_
+#define _XEN_LIB_DEBUG_H_
+
+#ifndef MODULE_NAME
+#define MODULE_NAME ""
+#endif
+
+#ifdef __KERNEL__
+#include <linux/config.h>
+#include <linux/kernel.h>
+
+#ifdef DEBUG
+
+#define dprintf(fmt, args...) printk(KERN_DEBUG   "[DBG] " MODULE_NAME ">%s" fmt, __FUNCTION__, ##args)
+#define wprintf(fmt, args...) printk(KERN_WARNING "[WRN] " MODULE_NAME ">%s" fmt, __FUNCTION__, ##args)
+#define iprintf(fmt, args...) printk(KERN_INFO    "[INF] " MODULE_NAME ">%s" fmt, __FUNCTION__, ##args)
+#define eprintf(fmt, args...) printk(KERN_ERR     "[ERR] " MODULE_NAME ">%s" fmt, __FUNCTION__, ##args)
+
+#else
+
+#define dprintf(fmt, args...) do {} while(0)
+#define wprintf(fmt, args...) printk(KERN_WARNING "[WRN] " MODULE_NAME fmt, ##args)
+#define iprintf(fmt, args...) printk(KERN_INFO    "[INF] " MODULE_NAME fmt, ##args)
+#define eprintf(fmt, args...) printk(KERN_ERR     "[ERR] " MODULE_NAME fmt, ##args)
+
+#endif
+
+#else
+
+#include <stdio.h>
+
+#ifdef DEBUG
+
+#define dprintf(fmt, args...) fprintf(stdout, "[DBG] " MODULE_NAME ">%s" fmt, __FUNCTION__, ##args)
+#define wprintf(fmt, args...) fprintf(stderr, "[WRN] " MODULE_NAME ">%s" fmt, __FUNCTION__, ##args)
+#define iprintf(fmt, args...) fprintf(stderr, "[INF] " MODULE_NAME ">%s" fmt, __FUNCTION__, ##args)
+#define eprintf(fmt, args...) fprintf(stderr, "[ERR] " MODULE_NAME ">%s" fmt, __FUNCTION__, ##args)
+
+#else
+
+#define dprintf(fmt, args...) do {} while(0)
+#define wprintf(fmt, args...) fprintf(stderr, "[WRN] " MODULE_NAME fmt, ##args)
+#define iprintf(fmt, args...) fprintf(stderr, "[INF] " MODULE_NAME fmt, ##args)
+#define eprintf(fmt, args...) fprintf(stderr, "[ERR] " MODULE_NAME fmt, ##args)
+
+#endif
+
+#endif
+
+/** Print format for an IP address.
+ * See NIPQUAD(), HIPQUAD()
+ */
+#define IPFMT "%u.%u.%u.%u"
+
+#endif /* ! _XEN_LIB_DEBUG_H_ */
diff --git a/tools/lib/enum.c b/tools/lib/enum.c
new file mode 100644 (file)
index 0000000..95f6e31
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2002, 2004 Mike Wray <mike.wray@hp.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of the
+ * License, or  (at your option) any later version. This library is 
+ * distributed in the  hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#ifdef __KERNEL__
+#include <linux/errno.h>
+#else
+#include <errno.h>
+#endif
+
+#include "sys_string.h"
+#include "enum.h"
+
+/** Map an enum name to its value using a table.
+ *
+ * @param name enum name
+ * @param defs enum definitions
+ * @return enum value or -1 if not known
+ */
+int enum_name_to_val(char *name, EnumDef *defs){
+    int val = -1;
+    for(; defs->name; defs++){
+       if(!strcmp(defs->name, name)){
+           val = defs->val;
+           break;
+       }
+    }
+    return val;
+}
+
+/** Map an enum value to its name using a table.
+ *
+ * @param val enum value
+ * @param defs enum definitions
+ * @param defs_n number of definitions
+ * @return enum name or NULL if not known
+ */
+char *enum_val_to_name(int val, EnumDef *defs){
+    char *name = NULL;
+    for(; defs->name; defs++){
+       if(val == defs->val){
+           name = defs->name;
+           break;
+       }
+    }
+    return name;
+}
+
diff --git a/tools/lib/enum.h b/tools/lib/enum.h
new file mode 100644 (file)
index 0000000..db6e7b0
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2002, 2004 Mike Wray <mike.wray@hp.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of the
+ * License, or  (at your option) any later version. This library is 
+ * distributed in the  hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#ifndef _XEN_LIB_ENUM_H_
+#define _XEN_LIB_ENUM_H_
+
+/** Mapping of an enum value to a name. */
+typedef struct EnumDef {
+    int val;
+    char *name;
+} EnumDef;
+
+extern int enum_name_to_val(char *name, EnumDef *defs);
+extern char *enum_val_to_name(int val, EnumDef *defs);
+
+#endif /* _XEN_LIB_ENUM_H_ */
diff --git a/tools/lib/file_stream.c b/tools/lib/file_stream.c
new file mode 100644 (file)
index 0000000..40391f7
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2001 - 2004 Mike Wray <mike.wray@hp.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/** @file
+ * An IOStream implementation using FILE*.
+ */
+#ifndef __KERNEL__
+#include <stdio.h>
+#include <stdlib.h>
+#include "allocate.h"
+#include "file_stream.h"
+
+static int file_read(IOStream *s, void *buf, size_t n);
+static int file_write(IOStream *s, const void *buf, size_t n);
+static int file_error(IOStream *s);
+static int file_close(IOStream *s);
+static void file_free(IOStream *s);
+static int file_flush(IOStream *s);
+
+/** Methods used by a FILE* IOStream. */
+static const IOMethods file_methods = {
+    read:  file_read,
+    write: file_write,
+    error: file_error,
+    close: file_close,
+    free:  file_free,
+    flush: file_flush,
+};
+
+/** IOStream for stdin. */
+static IOStream _iostdin = {
+    methods: &file_methods,
+    data: (void*)1,
+};
+
+/** IOStream for stdout. */
+static IOStream _iostdout = {
+    methods: &file_methods,
+    data: (void*)2,
+};
+
+/** IOStream for stderr. */
+static IOStream _iostderr = {
+    methods: &file_methods,
+    data: (void*)3,
+};
+
+/** IOStream for stdin. */
+IOStream *iostdin = &_iostdin;
+
+/** IOStream for stdout. */
+IOStream *iostdout = &_iostdout;
+
+/** IOStream for stderr. */
+IOStream *iostderr = &_iostderr;
+
+/** Get the underlying FILE*.
+ * 
+ * @param s file stream
+ * @return the stream s wraps
+ */
+static inline FILE *get_file(IOStream *s){
+    switch((long)s->data){
+    case 1: s->data = stdin; break;
+    case 2: s->data = stdout; break;
+    case 3: s->data = stderr; break;
+    }
+    return (FILE*)s->data;
+}
+
+/** Control buffering on the underlying stream, like setvbuf().
+ *
+ * @param io file stream
+ * @param buf buffer
+ * @param mode buffering mode (see man setvbuf())
+ * @param size buffer size
+ * @return 0 on success, non-zero otherwise
+ */
+int file_stream_setvbuf(IOStream *io, char *buf, int mode, size_t size){
+    return setvbuf(get_file(io), buf, mode, size);
+}
+
+/** Write to the underlying stream using fwrite();
+ *
+ * @param stream input
+ * @param buf where to put input
+ * @param n number of bytes to write
+ * @return number of bytes written
+ */
+static int file_write(IOStream *s, const void *buf, size_t n){
+    return fwrite(buf, 1, n, get_file(s));
+}
+
+/** Read from the underlying stream using fread();
+ *
+ * @param stream input
+ * @param buf where to put input
+ * @param n number of bytes to read
+ * @return number of bytes read
+ */
+static int file_read(IOStream *s, void *buf, size_t n){
+    return fread(buf, 1, n, get_file(s));
+}
+
+/** Fush the underlying stream using fflush().
+ *
+ * @param s file stream
+ * @return 0 on success, error code otherwise
+ */
+static int file_flush(IOStream *s){
+    return fflush(get_file(s));
+}
+
+/** Check if a stream has an error.
+ *
+ * @param s file stream
+ * @return 1 if has an error, 0 otherwise
+ */
+static int file_error(IOStream *s){
+    return ferror(get_file(s));
+}
+
+/** Close a file stream.
+ *
+ * @param s file stream to close
+ * @return result of the close
+ */
+static int file_close(IOStream *s){
+    return fclose(get_file(s));
+}
+
+/** Free a file stream.
+ *
+ * @param s file stream
+ */
+static void file_free(IOStream *s){
+    // Do nothing - fclose does it all?
+}
+
+/** Create an IOStream for a stream.
+ *
+ * @param f stream to wrap
+ * @return new IOStream using f for i/o
+ */
+IOStream *file_stream_new(FILE *f){
+    IOStream *io = ALLOCATE(IOStream);
+    if(io){
+       io->methods = &file_methods;
+       io->data = (void*)f;
+    }
+    return io;
+}
+
+/** IOStream version of fopen().
+ *
+ * @param file name of the file to open
+ * @param flags giving the mode to open in (as for fopen())
+ * @return new stream for the open file, or 0 if failed
+ */
+IOStream *file_stream_fopen(const char *file, const char *flags){
+    IOStream *io = 0;
+    FILE *fin = fopen(file, flags);
+    if(fin){
+       io = file_stream_new(fin);
+       if(!io){
+           fclose(fin);
+           //free(fin); // fclose frees ?
+       }
+    }
+    return io;
+}
+
+/** IOStream version of fdopen().
+ *
+ * @param fd file descriptor
+ * @param flags giving the mode to open in (as for fdopen())
+ * @return new stream for the open file, or 0 if failed
+ */
+IOStream *file_stream_fdopen(int fd, const char *flags){
+    IOStream *io = 0;
+    FILE *fin = fdopen(fd, flags);
+    if(fin){
+       io = file_stream_new(fin);
+    }
+    return io;
+}
+#endif
diff --git a/tools/lib/file_stream.h b/tools/lib/file_stream.h
new file mode 100644 (file)
index 0000000..36a0f92
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2001 - 2004 Mike Wray <mike.wray@hp.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef _XEN_LIB_FILE_STREAM_H_
+#define _XEN_LIB_FILE_STREAM_H_
+
+#ifndef __KERNEL__
+#include "iostream.h"
+#include <stdio.h>
+
+extern IOStream *file_stream_new(FILE *f);
+extern IOStream *file_stream_fopen(const char *file, const char *flags);
+extern IOStream *file_stream_fdopen(int fd, const char *flags);
+extern IOStream get_stream_stdout(void);
+extern IOStream get_stream_stderr(void);
+extern IOStream get_stream_stdin(void);
+
+extern int file_stream_setvbuf(IOStream *io, char *buf, int mode, size_t size);
+#endif
+#endif /* !_XEN_LIB_FILE_STREAM_H_ */
diff --git a/tools/lib/gzip_stream.c b/tools/lib/gzip_stream.c
new file mode 100644 (file)
index 0000000..1e67b4d
--- /dev/null
@@ -0,0 +1,185 @@
+/* $Id: gzip_stream.c,v 1.4 2003/09/30 15:22:53 mjw Exp $ */
+/*
+ * Copyright (C) 2003 Hewlett-Packard Company.
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/** @file
+ * An IOStream implementation using zlib gzFile to provide
+ * compression and decompression.
+ */
+#ifndef __KERNEL__
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "zlib.h"
+
+extern FILE* gzfile(gzFile file);
+
+#include "allocate.h"
+#include "gzip_stream.h"
+
+static int gzip_read(IOStream *s, void *buf, size_t n);
+static int gzip_write(IOStream *s, const void *buf, size_t n);
+static int gzip_error(IOStream *s);
+static int gzip_close(IOStream *s);
+static void gzip_free(IOStream *s);
+static int gzip_flush(IOStream *s);
+
+/** Methods used by a gzFile* IOStream. */
+static const IOMethods gzip_methods = {
+    read: gzip_read,
+    write: gzip_write,
+    error: gzip_error,
+    close: gzip_close,
+    free:  gzip_free,
+    flush: gzip_flush,
+};
+
+/** Get the underlying gzFile*.
+ * 
+ * @param s gzip stream
+ * @return the stream s wraps
+ */
+static inline gzFile get_gzfile(IOStream *s){
+    return (gzFile)s->data;
+}
+
+/** Control buffering on the underlying stream, like setvbuf().
+ *
+ * @param io gzip stream
+ * @param buf buffer
+ * @param mode buffering mode (see man setvbuf())
+ * @param size buffer size
+ * @return 0 on success, non-zero otherwise
+ */
+int gzip_stream_setvbuf(IOStream *io, char *buf, int mode, size_t size){
+    return setvbuf(gzfile(get_gzfile(io)), buf, mode, size);
+}
+
+/** Write to the underlying stream.
+ *
+ * @param stream destination
+ * @param buf data
+ * @param n number of bytes to write
+ * @return number of bytes written
+ */
+static int gzip_write(IOStream *s, const void *buf, size_t n){
+    return gzwrite(get_gzfile(s), (void*)buf, n);
+}
+
+/** Read from the underlying stream.
+ *
+ * @param stream input
+ * @param buf where to put input
+ * @param n number of bytes to read
+ * @return number of bytes read
+ */
+static int gzip_read(IOStream *s, void *buf, size_t n){
+    return gzread(get_gzfile(s), buf, n);
+}
+
+/** Flush the underlying stream.
+ *
+ * @param s gzip stream
+ * @return 0 on success, error code otherwise
+ */
+static int gzip_flush(IOStream *s){
+    //return gzflush(get_gzfile(s), Z_NO_FLUSH);
+    return gzflush(get_gzfile(s), Z_SYNC_FLUSH);
+    //return gzflush(get_gzfile(s), Z_FULL_FLUSH);
+}
+
+/** Check if a stream has an error.
+ *
+ * @param s gzip stream
+ * @return 1 if has an error, 0 otherwise
+ */
+static int gzip_error(IOStream *s){
+    int err;
+    gzFile *gz = get_gzfile(s);
+    gzerror(gz, &err);
+    return (err == Z_ERRNO ? ferror(gzfile(gz)) : err);
+}
+
+/** Close a gzip stream.
+ *
+ * @param s gzip stream to close
+ * @return result of the close
+ */
+static int gzip_close(IOStream *s){
+    return gzclose(get_gzfile(s));
+}
+
+/** Free a gzip stream.
+ *
+ * @param s gzip stream
+ */
+static void gzip_free(IOStream *s){
+    // Do nothing - fclose does it all?
+}
+
+/** Create an IOStream for a gzip stream.
+ *
+ * @param f stream to wrap
+ * @return new IOStream using f for i/o
+ */
+IOStream *gzip_stream_new(gzFile *f){
+    IOStream *io = ALLOCATE(IOStream);
+    if(io){
+       io->methods = &gzip_methods;
+       io->data = (void*)f;
+    }
+    return io;
+}
+
+/** IOStream version of fopen().
+ *
+ * @param file name of the file to open
+ * @param flags giving the mode to open in (as for fopen())
+ * @return new stream for the open file, or NULL if failed
+ */
+IOStream *gzip_stream_fopen(const char *file, const char *flags){
+    IOStream *io = NULL;
+    gzFile *fgz;
+    fgz = gzopen(file, flags);
+    if(fgz){
+       io = gzip_stream_new(fgz);
+       if(!io){
+           gzclose(fgz);
+           //free(fgz); // gzclose frees ?
+       }
+    }
+    return io;
+}
+
+/** IOStream version of fdopen().
+ *
+ * @param fd file descriptor
+ * @param flags giving the mode to open in (as for fdopen())
+ * @return new stream for the open file, or NULL if failed
+ */
+IOStream *gzip_stream_fdopen(int fd, const char *flags){
+    IOStream *io = NULL;
+    gzFile *fgz;
+    fgz = gzdopen(fd, flags);
+    if(fgz){
+       io = gzip_stream_new(fgz);
+    }
+    return io;
+}
+#endif
diff --git a/tools/lib/gzip_stream.h b/tools/lib/gzip_stream.h
new file mode 100644 (file)
index 0000000..63acb8c
--- /dev/null
@@ -0,0 +1,33 @@
+#/* $Id: gzip_stream.h,v 1.3 2003/09/30 15:22:53 mjw Exp $ */
+/*
+ * Copyright (C) 2003 Hewlett-Packard Company.
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef _SP_GZIP_STREAM_H_
+#define _SP_GZIP_STREAM_H_
+
+#ifndef __KERNEL__
+#include "iostream.h"
+#include "zlib.h"
+
+extern IOStream *gzip_stream_new(gzFile *f);
+extern IOStream *gzip_stream_fopen(const char *file, const char *flags);
+extern IOStream *gzip_stream_fdopen(int fd, const char *flags);
+
+extern int gzip_stream_setvbuf(IOStream *io, char *buf, int mode, size_t size);
+#endif
+#endif /* !_SP_FILE_STREAM_H_ */
diff --git a/tools/lib/hash_table.c b/tools/lib/hash_table.c
new file mode 100644 (file)
index 0000000..13da946
--- /dev/null
@@ -0,0 +1,640 @@
+/*
+ * Copyright (C) 2001 - 2004 Mike Wray <mike.wray@hp.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifdef __KERNEL__
+#  include <linux/config.h>
+#  include <linux/module.h>
+#  include <linux/kernel.h>
+#  include <linux/errno.h>
+#else
+#  include <errno.h>
+#  include <stddef.h>
+#endif
+
+//#include <limits.h>
+
+#include "allocate.h"
+#include "hash_table.h"
+
+/** @file
+ * Base support for hashtables.
+ *
+ * Hash codes are reduced modulo the number of buckets to index tables,
+ * so there is no need for hash functions to limit the range of hashcodes.
+ * In fact it is assumed that hashcodes do not change when the number of
+ * buckets in the table changes.
+ */
+
+/*==========================================================================*/
+/** Number of bits in half a word. */
+//#if __WORDSIZE == 64
+//#define HALF_WORD_BITS 32
+//#else
+#define HALF_WORD_BITS 16
+//#endif
+
+/** Mask for lo half of a word. On 32-bit this is 
+ * (1<<16) - 1 = 65535 = 0xffff
+ * It's 4294967295 = 0xffffffff on 64-bit.
+ */
+#define LO_HALF_MASK ((1 << HALF_WORD_BITS) - 1)
+
+/** Get the lo half of a word. */
+#define LO_HALF(x) ((x) & LO_HALF_MASK)
+
+/** Get the hi half of a word. */
+#define HI_HALF(x) ((x) >> HALF_WORD_BITS)
+
+/** Do a full hash on both inputs, using DES-style non-linear scrambling.
+ * Both inputs are replaced with the results of the hash.
+ *
+ * @param pleft input/output word
+ * @param pright input/output word
+ */
+void pseudo_des(unsigned long *pleft, unsigned long *pright){
+    // Bit-rich mixing constant.
+    static const unsigned long a_mixer[] = {
+        0xbaa96887L, 0x1e17d32cL, 0x03bcdc3cL, 0x0f33d1b2L, };
+
+    // Bit-rich mixing constant.
+    static const unsigned long b_mixer[] = {
+        0x4b0f3b58L, 0xe874f0c3L, 0x6955c5a6L, 0x55a7ca46L, };
+
+    // Number of iterations - must be 2 or 4.
+    static const int ncycle = 4;
+    //static const int ncycle = 2;
+
+    unsigned long left = *pleft, right = *pright;
+    unsigned long v, v_hi, v_lo;
+    int i;
+
+    for(i=0; i<ncycle; i++){
+        // Flip some bits in right to get v.
+        v = right;
+        v ^= a_mixer[i];
+        // Get lo and hi halves of v.
+        v_lo = LO_HALF(v);
+        v_hi = HI_HALF(v);
+        // Non-linear mix of the halves of v.
+        v = ((v_lo * v_lo) + ~(v_hi * v_hi));
+        // Swap the halves of v.
+        v = (HI_HALF(v) | (LO_HALF(v) << HALF_WORD_BITS));
+        // Flip some bits.
+        v ^= b_mixer[i];
+        // More non-linear mixing.
+        v += (v_lo * v_hi);
+        v ^= left;
+        left = right;
+        right = v;
+    }
+    *pleft = left;
+    *pright = right;
+}
+
+/** Hash a string.
+ *
+ * @param s input to hash
+ * @return hashcode
+ */
+Hashcode hash_string(char *s){
+    Hashcode h = 0;
+    if(s){
+        for( ; *s; s++){
+            h = hash_2ul(h, *s);
+        }
+    }
+    return h;
+}
+
+/** Get the bucket for a hashcode in a hash table.
+ *
+ * @param table to get bucket from
+ * @param hashcode to get bucket for
+ * @return bucket
+ */
+inline HTBucket * get_bucket(HashTable *table, Hashcode hashcode){
+    return table->buckets + (hashcode % table->buckets_n);
+}
+
+/** Initialize a hash table.
+ * Can be safely called more than once.
+ *
+ * @param table to initialize
+ */
+void HashTable_init(HashTable *table){
+    int i;
+
+    if(!table->init_done){
+        table->init_done = 1;
+        table->next_id = 0;
+        for(i=0; i<table->buckets_n; i++){
+            HTBucket *bucket = get_bucket(table, i);
+            bucket->head = 0;
+            bucket->count = 0;
+        }
+        table->entry_count = 0;
+    }
+}
+
+/** Allocate a new hashtable.
+ * If the number of buckets is not positive the default is used.
+ * The number of buckets should usually be prime.
+ *
+ * @param buckets_n number of buckets
+ * @return new hashtable or null
+ */
+HashTable *HashTable_new(int buckets_n){
+    HashTable *z = ALLOCATE(HashTable);
+    if(!z) goto exit;
+    if(buckets_n <= 0){
+        buckets_n = HT_BUCKETS_N;
+    }
+    z->buckets = (HTBucket*)allocate(buckets_n * sizeof(HTBucket));
+    if(!z->buckets){
+        deallocate(z);
+        z = 0;
+        goto exit;
+    }
+    z->buckets_n = buckets_n;
+    HashTable_init(z);
+  exit:
+    return z;
+}
+
+/** Free a hashtable.
+ * Any entries are removed and freed.
+ *
+ * @param h hashtable (ignored if null)
+ */
+void HashTable_free(HashTable *h){
+    if(h){
+        HashTable_clear(h);
+        deallocate(h->buckets);
+        deallocate(h);
+    }
+}
+
+/** Push an entry on the list in the bucket for a given hashcode.
+ *
+ * @param table to add entry to
+ * @param hashcode for the entry
+ * @param entry to add
+ */
+static inline void push_on_bucket(HashTable *table, Hashcode hashcode,
+                                 HTEntry *entry){
+    HTBucket *bucket;
+    HTEntry *old_head;
+
+    bucket = get_bucket(table, hashcode);
+    old_head = bucket->head;
+    bucket->count++;
+    bucket->head = entry;
+    entry->next = old_head;
+}
+
+/** Change the number of buckets in a hashtable.
+ * No-op if the number of buckets is not positive.
+ * Existing entries are reallocated to buckets based on their hashcodes.
+ * The table is unmodified if the number of buckets cannot be changed.
+ *
+ * @param table hashtable
+ * @param buckets_n new number of buckets
+ * @return 0 on success, error code otherwise
+ */
+int HashTable_set_buckets_n(HashTable *table, int buckets_n){
+    int err = 0;
+    HTBucket *old_buckets = table->buckets;
+    int old_buckets_n = table->buckets_n;
+    int i;
+
+    if(buckets_n <= 0){
+        err = -EINVAL;
+        goto exit;
+    }
+    table->buckets = (HTBucket*)allocate(buckets_n * sizeof(HTBucket));
+    if(!table->buckets){
+        err = -ENOMEM;
+        table->buckets = old_buckets;
+        goto exit;
+    }
+    table->buckets_n = buckets_n;
+    for(i=0; i<old_buckets_n; i++){
+        HTBucket *bucket = old_buckets + i;
+        HTEntry *entry, *next;
+        for(entry = bucket->head; entry; entry = next){
+            next = entry->next;
+            push_on_bucket(table, entry->hashcode, entry);
+        }
+    }
+    deallocate(old_buckets);
+  exit:
+    return err;
+}
+
+/** Adjust the number of buckets so the table is neither too full nor too empty.
+ * The table is unmodified if adjusting fails.
+ *
+ * @param table hash table
+ * @param buckets_min minimum number of buckets (use default if 0 or negative)
+ * @return 0 on success, error code otherwise
+ */
+int HashTable_adjust(HashTable *table, int buckets_min){
+    int buckets_n = 0;
+    int err = 0;
+    if(buckets_min <= 0) buckets_min = HT_BUCKETS_N;
+    if(table->entry_count >= table->buckets_n){
+        // The table is dense - expand it.
+        buckets_n = 2 * table->buckets_n;
+    } else if((table->buckets_n > buckets_min) &&
+              (4 * table->entry_count < table->buckets_n)){
+        // The table is more than minimum size and sparse - shrink it.
+        buckets_n = 2 * table->entry_count;
+        if(buckets_n < buckets_min) buckets_n = buckets_min;
+    }
+    if(buckets_n){
+        err = HashTable_set_buckets_n(table, buckets_n);
+    }
+    return err;
+}
+
+/** Allocate a new entry for a given value.
+ *
+ * @param value to put in the entry
+ * @return entry, or 0 on failure
+ */
+HTEntry * HTEntry_new(Hashcode hashcode, void *key, void *value){
+    HTEntry *z = ALLOCATE(HTEntry);
+    if(z){
+        z->hashcode = hashcode;
+        z->key = key;
+        z->value = value;
+    }
+    return z;
+}
+
+/** Free an entry.
+ *
+ * @param z entry to free
+ */
+inline void HTEntry_free(HTEntry *z){
+    if(z){
+        deallocate(z);
+    }
+}
+
+/** Free an entry in a hashtable.
+ * The table's entry_free_fn is used is defined, otherwise 
+ * the HTEntry itself is freed.
+ *
+ * @param table hashtable
+ * @param entry to free
+ */
+inline void HashTable_free_entry(HashTable *table, HTEntry *entry){
+    if(!entry)return;
+    if(table && table->entry_free_fn){
+        table->entry_free_fn(table, entry);
+    } else {
+        HTEntry_free(entry);
+    }
+}
+
+/** Get the first entry satisfying a test from the bucket for the
+ * given hashcode.
+ *
+ * @param table to look in
+ * @param hashcode indicates the bucket
+ * @param test_fn test to apply to elements
+ * @param arg first argument to calls to test_fn
+ * @return entry found, or 0
+ */
+inline HTEntry * HashTable_find_entry(HashTable *table, Hashcode hashcode,
+                                     TableTestFn *test_fn, TableArg arg){
+    HTBucket *bucket;
+    HTEntry *entry = 0;
+    HTEntry *next;
+
+    bucket = get_bucket(table, hashcode);
+    for(entry = bucket->head; entry; entry = next){
+        next = entry->next;
+        if(test_fn(arg, table, entry)){
+            break;
+        }
+    }
+    return entry;
+}
+
+/** Test hashtable keys for equality.
+ * Uses the table's key_equal_fn if defined, otherwise pointer equality.
+ *
+ * @param key1 key to compare
+ * @param key2 key to compare
+ * @return 1 if equal, 0 otherwise
+ */
+inline int HashTable_key_equal(HashTable *table, void *key1, void *key2){
+    return (table->key_equal_fn ? table->key_equal_fn(key1, key2) : key1==key2);
+}
+
+/** Compute the hashcode of a hashtable key.
+ * The table's key_hash_fn is used if defined, otherwise the address of
+ * the key is hashed.
+ *
+ * @param table hashtable
+ * @param key to hash
+ * @return hashcode
+ */
+inline Hashcode HashTable_key_hash(HashTable *table, void *key){
+    return (table->key_hash_fn ? table->key_hash_fn(key) : hash_ul((unsigned long)key));
+}
+
+/** Test if an entry has a given key.
+ *
+ * @param arg containing key to test for
+ * @param table the entry is in
+ * @param entry to test
+ * @return 1 if the entry has the key, 0 otherwise
+ */
+static inline int has_key(TableArg arg, HashTable *table, HTEntry *entry){
+    return HashTable_key_equal(table, arg.ptr, entry->key);
+}
+
+/** Get an entry with a given key.
+ *
+ * @param table to search
+ * @param key to look for
+ * @return entry if found, null otherwise
+ */
+#if 0
+inline HTEntry * HashTable_get_entry(HashTable *table, void *key){
+    TableArg arg = { ptr: key };
+    return HashTable_find_entry(table, HashTable_key_hash(table, key), has_key, arg);
+}
+#else
+inline HTEntry * HashTable_get_entry(HashTable *table, void *key){
+    Hashcode hashcode;
+    HTBucket *bucket;
+    HTEntry *entry = 0;
+    HTEntry *next;
+
+    hashcode = HashTable_key_hash(table, key);
+    bucket = get_bucket(table, hashcode);
+    for(entry = bucket->head; entry; entry = next){
+        next = entry->next;
+        if(HashTable_key_equal(table, key, entry->key)){
+            break;
+        }
+    }
+    return entry;
+}
+#endif
+
+/** Get the value of an entry with a given key.
+ *
+ * @param table to search
+ * @param key to look for
+ * @return value if an entry was found, null otherwise
+ */
+inline void * HashTable_get(HashTable *table, void *key){
+    HTEntry *entry = HashTable_get_entry(table, key);
+    return (entry ? entry->value : 0);
+}
+
+/** Print the buckets in a table.
+ *
+ * @param table to print
+ */
+void show_buckets(HashTable *table, IOStream *io){
+    int i,j ;
+    IOStream_print(io, "entry_count=%d buckets_n=%d\n", table->entry_count, table->buckets_n);
+    for(i=0; i<table->buckets_n; i++){
+        if(0 || table->buckets[i].count>0){
+            IOStream_print(io, "bucket %3d %3d %10p ", i,
+                        table->buckets[i].count,
+                        table->buckets[i].head);
+            for(j = table->buckets[i].count; j>0; j--){
+                IOStream_print(io, "+");
+            }
+            IOStream_print(io, "\n");
+        }
+    }
+    HashTable_print(table, io); 
+}
+    
+/** Print an entry in a table.
+ *
+ * @param entry to print
+ * @param arg a pointer to an IOStream to print to
+ * @return 0
+ */
+static int print_entry(TableArg arg, HashTable *table, HTEntry *entry){
+    IOStream *io = (IOStream*)arg.ptr;
+    IOStream_print(io, " b=%4lx h=%08lx i=%08lx |-> e=%8p k=%8p v=%8p\n",
+                entry->hashcode % table->buckets_n,
+                entry->hashcode,
+                entry->index,
+                entry, entry->key, entry->value);
+    return 0;
+}
+
+/** Print a hash table.
+ *
+ * @param table to print
+ */
+void HashTable_print(HashTable *table, IOStream *io){
+    IOStream_print(io, "{\n");
+    HashTable_map(table, print_entry, (TableArg){ ptr: io });
+    IOStream_print(io, "}\n");
+}
+/*==========================================================================*/
+
+/** Get the next entry id to use for a table.
+ *
+ * @param table hash table
+ * @return non-zero entry id
+ */
+static inline unsigned long get_next_id(HashTable *table){
+    unsigned long id;
+
+    if(table->next_id == 0){
+        table->next_id = 1;
+    }
+    id = table->next_id++;
+    return id;
+}
+
+/** Add an entry to the bucket for the
+ * given hashcode.
+ *
+ * @param table to insert in
+ * @param hashcode indicates the bucket
+ * @param key to add an entry for
+ * @param value to add an entry for
+ * @return entry on success, 0 on failure
+ */
+inline HTEntry * HashTable_add_entry(HashTable *table, Hashcode hashcode, void *key, void *value){
+    HTEntry *entry = HTEntry_new(hashcode, key, value);
+    if(entry){
+        entry->index = get_next_id(table);
+        push_on_bucket(table, hashcode, entry);
+        table->entry_count++;
+    }
+    return entry;
+}
+
+/** Move the front entry for a bucket to the correct point in the bucket order as
+ * defined by the order function. If this is called every time a new entry is added
+ * the bucket will be maintained in sorted order.
+ *
+ * @param table to modify
+ * @param hashcode indicates the bucket
+ * @param order entry comparison function
+ * @return 0 if an entry was moved, 1 if not
+ */
+int HashTable_order_bucket(HashTable *table, Hashcode hashcode, TableOrderFn *order){
+    HTEntry *new_entry = NULL, *prev = NULL, *entry = NULL;
+    HTBucket *bucket;
+    int err = 1;
+
+    bucket = get_bucket(table, hashcode);
+    new_entry = bucket->head;
+    if(!new_entry || !new_entry->next) goto exit;
+    for(entry = new_entry->next; entry; prev = entry, entry = entry->next){
+        if(order(new_entry, entry) <= 0) break;
+    }
+    if(prev){
+        err = 0;
+        bucket->head = new_entry->next; 
+        new_entry->next = entry;
+        prev->next = new_entry;
+    }
+  exit:
+    return err;
+}
+
+/** Add an entry to a hashtable.
+ * The entry is added to the bucket for its key's hashcode.
+ *
+ * @param table to insert in
+ * @param key to add an entry for
+ * @param value to add an entry for
+ * @return entry on success, 0 on failure
+ */
+inline HTEntry * HashTable_add(HashTable *table, void *key, void *value){
+    return HashTable_add_entry(table, HashTable_key_hash(table, key), key, value);
+}
+
+
+/** Remove entries satisfying a test from the bucket for the
+ * given hashcode. 
+ *
+ * @param table to remove from
+ * @param hashcode indicates the bucket
+ * @param test_fn test to apply to elements
+ * @param arg first argument to calls to test_fn
+ * @return number of entries removed
+ */
+inline int HashTable_remove_entry(HashTable *table, Hashcode hashcode,
+                                 TableTestFn *test_fn, TableArg arg){
+    HTBucket *bucket;
+    HTEntry *entry, *prev = 0, *next;
+    int removed_count = 0;
+
+    bucket = get_bucket(table, hashcode);
+    for(entry = bucket->head; entry; entry = next){
+        next = entry->next;
+        if(test_fn(arg, table, entry)){
+            if(prev){
+                prev->next = next;
+            } else {
+                bucket->head = next;
+            }
+            bucket->count--;
+            table->entry_count--;
+            removed_count++;
+            HashTable_free_entry(table, entry);
+            entry = 0;
+        }
+        prev = entry;
+    }
+    return removed_count;
+}
+
+/** Remove entries with a given key. 
+ *
+ * @param table to remove from
+ * @param key of entries to remove
+ * @return number of entries removed
+ */
+inline int HashTable_remove(HashTable *table, void *key){
+#if 1
+    Hashcode hashcode;
+    HTBucket *bucket;
+    HTEntry *entry, *prev = 0, *next;
+    int removed_count = 0;
+
+    hashcode = HashTable_key_hash(table, key);
+    bucket = get_bucket(table, hashcode);
+    for(entry = bucket->head; entry; entry = next){
+        next = entry->next;
+        if(HashTable_key_equal(table, key, entry->key)){
+            if(prev){
+                prev->next = next;
+            } else {
+                bucket->head = next;
+            }
+            bucket->count--;
+            table->entry_count--;
+            removed_count++;
+            HashTable_free_entry(table, entry);
+            entry = 0;
+        }
+        prev = entry;
+    }
+    return removed_count;
+#else
+    return HashTable_remove_entry(table, HashTable_key_hash(table, key),
+                                 has_key, (TableArg){ ptr: key});
+#endif
+}
+
+/** Remove (and free) all the entries in a bucket.
+ *
+ * @param bucket to clear
+ */
+static inline void bucket_clear(HashTable *table, HTBucket *bucket){
+    HTEntry *entry, *next;
+
+    for(entry = bucket->head; entry; entry = next){
+        next = entry->next;
+        HashTable_free_entry(table, entry);
+    }
+    bucket->head = 0;
+    table->entry_count -= bucket->count;
+    bucket->count = 0;
+}
+
+/** Remove (and free) all the entries in a table.
+ *
+ * @param table to clear
+ */
+void HashTable_clear(HashTable *table){
+    int i, n = table->buckets_n;
+
+    for(i=0; i<n; i++){
+        bucket_clear(table, table->buckets + i);
+    }
+}
diff --git a/tools/lib/hash_table.h b/tools/lib/hash_table.h
new file mode 100644 (file)
index 0000000..6d7e76f
--- /dev/null
@@ -0,0 +1,295 @@
+/* $Id: hash_table.h,v 1.1 2004/03/30 16:21:26 mjw Exp $ */
+/*
+ * Copyright (C) 2001 - 2004 Mike Wray <mike.wray@hp.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef _XEN_LIB_HASH_TABLE_H_
+#define _XEN_LIB_HASH_TABLE_H_
+
+#include "iostream.h"
+
+typedef unsigned long Hashcode;
+
+/** Type used to pass parameters to table functions. */
+typedef union TableArg {
+    unsigned long ul;
+    void *ptr;
+} TableArg;
+
+/** An entry in a bucket list. */
+typedef struct HTEntry {
+    /** Hashcode of the entry's key. */
+    Hashcode hashcode;
+    /** Identifier for this entry in the table. */
+    int index;
+    /** The key for this entry. */
+    void *key;
+    /** The value in this entry. */
+    void *value;
+    /** The next entry in the list. */
+    struct HTEntry *next;
+} HTEntry;
+
+/** A bucket in a rule table. */
+typedef struct HTBucket {
+    /** Number of entries in the bucket. */
+    int count;
+    /** First entry in the bucket (may be null). */
+    HTEntry *head;
+} HTBucket;
+
+/** Default number of buckets in a hash table.
+ * You want enough buckets so the lists in the buckets will typically be short.
+ * It's a good idea if this is prime, since that will help to spread hashcodes
+ * around the table.
+ */
+//#define HT_BUCKETS_N 1
+//#define HT_BUCKETS_N 3
+//#define HT_BUCKETS_N 7
+//#define HT_BUCKETS_N 17
+//#define HT_BUCKETS_N 97
+//#define HT_BUCKETS_N 211
+//#define HT_BUCKETS_N 401
+#define HT_BUCKETS_N 1021
+
+typedef struct HashTable HashTable;
+
+/** Type for a function used to select table entries. */
+typedef int TableTestFn(TableArg arg, HashTable *table, HTEntry *entry);
+
+/** Type for a function to map over table entries. */
+typedef int TableMapFn(TableArg arg, HashTable *table, HTEntry *entry);
+
+/** Type for a function to free table entries. */
+typedef void TableFreeFn(HashTable *table, HTEntry *entry);
+
+/** Type for a function to hash table keys. */
+typedef Hashcode TableHashFn(void *key);
+
+/** Type for a function to test table keys for equality. */
+typedef int TableEqualFn(void *key1, void *key2);
+
+/** Type for a function to order table entries. */
+typedef int TableOrderFn(HTEntry *e1, HTEntry *e2);
+
+/** General hash table.
+ * A hash table with a list in each bucket.
+ * Functions can be supplied for freeing entries, hashing keys, and comparing keys.
+ * These all default to 0, when default behaviour treating keys as integers is used.
+ */
+struct HashTable {
+    /** Flag indicating whether the table has been initialised. */
+    int init_done;
+    /** Next value for the id field in inserted rules. */
+    unsigned long next_id;
+    /** Number of buckets in the bucket array. */
+    int buckets_n;
+    /** Array of buckets, each with its own list. */
+    HTBucket *buckets;
+    /** Number of entries in the table. */
+    int entry_count;
+    /** Function to free keys and values in entries. */
+    TableFreeFn *entry_free_fn;
+    /** Function to hash keys. */
+    TableHashFn *key_hash_fn;
+    /** Function to compare keys for equality. */
+    TableEqualFn *key_equal_fn;
+    /** Place for the user of the table to hang extra data. */
+    void *user_data;
+};
+
+extern HashTable *HashTable_new(int bucket_n);
+extern void HashTable_free(HashTable *table);
+extern HTEntry * HTEntry_new(Hashcode hashcode, void *key, void *value);
+extern void HTEntry_free(HTEntry *entry);
+extern int HashTable_set_bucket_n(HashTable *table, int bucket_n);
+extern void HashTable_clear(HashTable *table);
+extern HTEntry * HashTable_add_entry(HashTable *table, Hashcode hashcode, void *key, void *value);
+extern HTEntry * HashTable_get_entry(HashTable *table, void *key);
+extern HTEntry * HashTable_add(HashTable *table, void *key, void *value);
+extern void * HashTable_get(HashTable *table, void *key);
+extern int HashTable_remove(HashTable *table, void *key);
+extern HTEntry * HashTable_find_entry(HashTable *table, Hashcode hashcode,
+                                      TableTestFn *test_fn, TableArg arg);
+extern int HashTable_remove_entry(HashTable *table, Hashcode hashcode,
+                                   TableTestFn *test_fn, TableArg arg);
+//extern int HashTable_map(HashTable *table, TableMapFn *map_fn, TableArg arg);
+extern void HashTable_print(HashTable *table, IOStream *out);
+extern int HashTable_set_buckets_n(HashTable *table, int buckets_n);
+extern int HashTable_adjust(HashTable *table, int buckets_min);
+extern void pseudo_des(unsigned long *pleft, unsigned long *pright);
+extern Hashcode hash_string(char *s);
+
+extern int HashTable_order_bucket(HashTable *table, Hashcode hashcode, TableOrderFn *order);
+
+/** Control whether to use hashing based on DES or simple
+ * hashing. DES hashing is `more random' but much more expensive.
+ */
+#define HASH_PSEUDO_DES 0
+
+/** Hash a long using a quick and dirty linear congruential random number generator.
+ *  See `Numerical Recipes in C', Chapter 7, "An Even Quicker Generator".
+ *
+ * @param a value to hash
+ * @return hashed input
+ */
+static inline unsigned long lcrng_hash(unsigned long a){
+    return (1664525L * a + 1013904223L);
+}
+
+/** Hash an unsigned long.
+ *
+ * @param a input to hash
+ * @return hashcode
+ */
+static inline Hashcode hash_ul(unsigned long a){
+#if HASH_PSEUDO_DES
+    unsigned long left = a;
+    unsigned long right = 0L;
+    pseudo_des(&left, &right);
+    return right;
+#else
+    a = lcrng_hash(a);
+    a = lcrng_hash(a);
+    return a;
+#endif
+}
+
+/** Hash two unsigned longs together.
+ *
+ * @param a input to hash
+ * @param b input to hash
+ * @return hashcode
+ */
+static inline Hashcode hash_2ul(unsigned long a, unsigned long b){
+#if HASH_PSEUDO_DES
+    unsigned long left = a;
+    unsigned long right = b;
+    pseudo_des(&left, &right);
+    return right;
+#else
+    a = lcrng_hash(a);
+    a ^= b;
+    a = lcrng_hash(a);
+    return a;
+#endif
+}
+
+/** Hash a hashcode and an unsigned long together.
+ *
+ * @param a input hashcode
+ * @param b input to hash
+ * @return hashcode
+ */
+static inline Hashcode hash_hul(Hashcode a, unsigned long b){
+#if HASH_PSEUDO_DES
+    unsigned long left = a;
+    unsigned long right = b;
+    pseudo_des(&left, &right);
+    return right;
+#else
+    a ^= b;
+    a = lcrng_hash(a);
+    return a;
+#endif
+}
+
+/** Macro to declare variables for HashTable_for_each() to use.
+ *
+ * @param entry variable that is set to entries in the table
+ */
+#define HashTable_for_decl(entry) \
+  HashTable *_var_table; \
+  HTBucket *_var_bucket; \
+  HTBucket *_var_end; \
+  HTEntry *_var_next; \
+  HTEntry *entry
+
+/** Macro to iterate over the entries in a hashtable.
+ * Must be in a scope where HashTable_for_decl() has been used to declare
+ * variables for it to use.
+ * The variable 'entry' is iterated over entries in the table.
+ * The code produced is syntactically a loop, so it must be followed by
+ * a loop body, typically some statements in braces:
+ * HashTable_for_each(entry, table){ ...loop body... }
+ *
+ * HashTable_for_each() and HashTable_for_decl() cannot be used for nested
+ * loops as variables will clash.
+ *
+ * @note The simplest way to code a direct loop over the entries in a hashtable
+ * is to use a loop over the buckets, with a nested loop over the entries
+ * in a bucket. Using this approach in a macro means the macro contains
+ * an opening brace, and calls to it must be followed by 2 braces!
+ * To avoid this the code has been restructured so that it is a for loop.
+ * So that statements could be used in the test expression of the for loop,
+ * we have used the gcc statement expression extension ({ ... }).
+ *
+ * @param entry variable to iterate over the entries
+ * @param table to iterate over (non-null)
+ */
+#define HashTable_for_each(entry, table) \
+  _var_table = table; \
+  _var_bucket = _var_table->buckets; \
+  _var_end = _var_bucket + _var_table->buckets_n; \
+  for(entry=0, _var_next=0; \
+      ({ if(_var_next){ \
+             entry = _var_next; \
+             _var_next = entry->next; \
+          } else { \
+             while(_var_bucket < _var_end){ \
+                 entry = _var_bucket->head; \
+                 _var_bucket++; \
+                 if(entry){ \
+                      _var_next = entry->next; \
+                      break; \
+                 } \
+             } \
+          }; \
+         entry; }); \
+      entry = _var_next )
+
+/** Map a function over the entries in a table.
+ * Mapping stops when the function returns a non-zero value.
+ * Uses the gcc statement expression extension ({ ... }).
+ *
+ * @param table to map over
+ * @param fn function to apply to entries
+ * @param arg first argument to call the function with
+ * @return 0 if fn always returned 0, first non-zero value otherwise
+ */
+#define HashTable_map(table, fn, arg) \
+  ({ HashTable_for_decl(_var_entry); \
+    TableArg _var_arg = arg; \
+    int _var_value = 0; \
+    HashTable_for_each(_var_entry, table){ \
+        if((_var_value = fn(_var_arg, _var_table, _var_entry))) break; \
+    } \
+    _var_value; })
+
+/** Cast x to the type for a key or value in a hash table.
+ * This avoids compiler warnings when using short integers
+ * as keys or values (especially on 64-bit platforms).
+ */
+#define HKEY(x) ((void*)(unsigned long)(x))
+
+/** Cast x from the type for a key or value in a hash table.
+ * to an unsigned long. This avoids compiler warnings when using
+ * short integers as keys or values (especially on 64-bit platforms).
+ */
+#define HVAL(x) ((unsigned long)(x))
+
+#endif /* !_XEN_LIB_HASH_TABLE_H_ */
diff --git a/tools/lib/iostream.c b/tools/lib/iostream.c
new file mode 100644 (file)
index 0000000..e998083
--- /dev/null
@@ -0,0 +1,37 @@
+#include "iostream.h"
+#include "sys_string.h"
+
+/** Print on a stream, like vfprintf().
+ *
+ * @param stream to print to
+ * @param format for the print (as fprintf())
+ * @param args arguments to print
+ * @return result code from the print
+ */
+int IOStream_vprint(IOStream *stream, const char *format, va_list args){
+  char buffer[1024];
+  int k = sizeof(buffer), n;
+
+  n = vsnprintf(buffer, k, (char*)format, args);
+  if(n < 0 || n > k ){
+      n = k;
+  }
+  n = IOStream_write(stream, buffer, n);
+  return n;
+}
+
+/** Print on a stream, like fprintf().
+ *
+ * @param stream to print to
+ * @param format for the print (as fprintf())
+ * @return result code from the print
+ */
+int IOStream_print(IOStream *stream, const char *format, ...){
+  va_list args;
+  int result = -1;
+
+  va_start(args, format);
+  result = IOStream_vprint(stream, format, args);
+  va_end(args);
+  return result;
+}
diff --git a/tools/lib/iostream.h b/tools/lib/iostream.h
new file mode 100644 (file)
index 0000000..5dbe14a
--- /dev/null
@@ -0,0 +1,243 @@
+#ifndef _XC_LINUX_SAVE_H_
+#define _XC_LINUX_SAVE_H_
+
+#include <stdarg.h>
+#include <stdint.h>
+#include <stddef.h>
+
+#ifdef __KERNEL__
+#include <linux/errno.h>
+#else
+#include <errno.h>
+#endif
+
+#include "allocate.h"
+
+/** End of input return value. */
+#define IOSTREAM_EOF -1
+
+/** An input/output abstraction.
+ */
+typedef struct IOStream IOStream;
+
+/** Record of the functions to use for operations on an
+ * IOStream implementation.
+ */
+typedef struct IOMethods {
+    /** Read function.  Called with the user data, buffer to read into
+     * and number of bytes to read.  Must return number of bytes read
+     * on success, less than zero on error.
+     */
+    int (*read)(IOStream *stream, void *buf, size_t n);
+
+    /** Write function. Called with user data, buffer to write and
+     * number of bytes to write. Must return number of bytes written on
+     * success, less than zero otherwise.
+     */
+    int (*write)(IOStream *stream, const void *buf, size_t n);
+
+    int (*flush)(IOStream *s);
+
+    int (*error)(IOStream *s);
+
+    int (*close)(IOStream *s);
+
+    void (*free)(IOStream *s);
+
+    void (*lock)(IOStream *s);
+    void (*unlock)(IOStream *s);
+
+} IOMethods;
+
+/** Abstract i/o object.
+ */
+struct IOStream {
+    /** Methods to use to implement operations. */
+    const IOMethods *methods;
+    /** Private state for the implementation. */
+    const void *data;
+    /** Flag indicating whether the stream is closed. */
+    int closed;
+    /** Number of bytes written. */
+    int written;
+    /** Number of bytes read. */
+    int read;
+};
+
+
+/** IOStream version of stdin. */
+extern IOStream *iostdin;
+
+/** IOStream version of stdout, */
+extern IOStream *iostdout;
+
+/** IOStream version of stderr. */
+extern IOStream *iostderr;
+
+extern int IOStream_print(IOStream *io, const char *format, ...);
+extern int IOStream_vprint(IOStream *io, const char *format, va_list args);
+
+/** Read from a stream.
+ *
+ * @param stream input
+ * @param buf where to put input
+ * @param n number of bytes to read
+ * @return if ok, number of bytes read, otherwise negative error code
+ */
+static inline int IOStream_read(IOStream *stream, void *buf, size_t n){
+    int result = 0;
+    if(stream->closed) goto exit;
+    if(!stream->methods || !stream->methods->read){
+        result = -EINVAL;
+        goto exit;
+    }
+    result = stream->methods->read(stream, buf, n);
+    if(result > 0){
+        stream->read += result;
+    }
+  exit:
+    return result;
+}
+
+/** Write to a stream.
+ *
+ * @param stream input
+ * @param buf where to put input
+ * @param n number of bytes to write
+ * @return if ok, number of bytes read, otherwise negative error code
+ */
+static inline int IOStream_write(IOStream *stream, const void *buf, size_t n){
+    int result = 0;
+    if(stream->closed) goto exit;
+    if(!stream->methods || !stream->methods->write){
+        result = -EINVAL;
+        goto exit;
+    }
+    result = stream->methods->write(stream, buf, n);
+    if(result > 0){
+        stream->written += result;
+    }
+  exit:
+    return result;
+}
+
+/** Flush the stream.
+ *
+ * @param stream stream
+ * @return 0 on success, IOSTREAM_EOF otherwise
+ */
+static inline int IOStream_flush(IOStream *stream){
+    int result = 0;
+    if(stream->closed){
+        result = IOSTREAM_EOF;
+    } else if(stream->methods->flush){
+        result = stream->methods->flush(stream);
+        if(result < 0) result = IOSTREAM_EOF;
+    }
+    return result;
+}
+
+/** Check whether the stream has an error.
+ *
+ * @param stream to check
+ * @return 1 for error, 0 otherwise
+ */
+static inline int IOStream_error(IOStream *stream){
+    int err = 0;
+    if(stream->methods && stream->methods->error){
+       err = stream->methods->error(stream);
+    }
+    return err;
+}
+
+/** Close the stream.
+ *
+ * @param stream to close
+ * @return 1 for error, 0 otherwise
+ */
+static inline int IOStream_close(IOStream *stream){
+    int err = 1;
+    if(stream->methods && stream->methods->close){
+        err = stream->methods->close(stream);
+    }
+    return err;
+}
+
+/** Test if the stream has been closed.
+ *
+ * @param stream to check
+ * @return 1 if closed, 0 otherwise
+ */
+static inline int IOStream_is_closed(IOStream *stream){
+  return stream->closed;
+}
+
+/** Free the memory used by the stream.
+ *
+ * @param stream to free
+ */
+static inline void IOStream_free(IOStream *stream){
+  if(stream->methods && stream->methods->free){
+    stream->methods->free(stream);
+  }
+  *stream = (IOStream){};
+  deallocate(stream);
+}
+
+
+/** Print a character to a stream, like fputc().
+ *
+ * @param stream to print to
+ * @param c character to print
+ * @return result code from the print
+ */
+static inline int IOStream_putc(IOStream *stream, int c){
+    int err;
+    unsigned char b = (unsigned char)c;
+    err = IOStream_write(stream, &b, 1);
+    if(err < 1){
+        err = IOSTREAM_EOF;
+    } else {
+        err = b;
+    }
+    return err;
+}
+
+/** Read from a stream, like fgetc().
+ *
+ * @param stream to read from
+ * @return IOSTREAM_EOF on error, character read otherwise
+ */
+static inline int IOStream_getc(IOStream *stream){
+    int err, rc;
+    unsigned char b;
+
+    err = IOStream_read(stream, &b, 1);
+    if(err < 1){
+        rc = IOSTREAM_EOF;
+    } else {
+        rc = b;
+    }
+    return rc;
+}
+
+/** Get number of bytes read.
+ *
+ * @param stream to get from
+ * @return number of bytes read
+ */
+static inline int IOStream_get_read(IOStream *stream){
+    return stream->read;
+}
+
+/** Get number of bytes written.
+ *
+ * @param stream to get from
+ * @return number of bytes written
+ */
+static inline int IOStream_get_written(IOStream *stream){
+    return stream->written;
+}
+
+
+#endif /* ! _XC_LINUX_SAVE_H_ */
diff --git a/tools/lib/kernel_stream.c b/tools/lib/kernel_stream.c
new file mode 100644 (file)
index 0000000..345b048
--- /dev/null
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2001 - 2004 Mike Wray <mike.wray@hp.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/** @file
+ * An IOStream implementation using printk() for output.
+ * Input is not implemented.
+ */
+#ifdef __KERNEL__
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#include "kernel_stream.h"
+#include "allocate.h"
+
+/** Number of characters in the output buffer.
+ * The kernel uses 1024 for printk, so that should suffice.
+ */
+#define BUF_N 1024
+
+/** State for a kernel stream. */
+typedef struct KernelData {
+    /** Stream lock. We need a lock to serialize access to the stream. */
+    spinlock_t lock;
+    /** Saved flags for locking. */
+    unsigned long flags;
+    /** Size of the output buffer. */
+    int buf_n;
+    /** Output buffer. */
+    char buf[BUF_N];
+} KernelData;
+
+static int kernel_write(IOStream *s, const char *msg, int n);
+static void kernel_free(IOStream *s);
+static void kernel_stream_lock(IOStream *s);
+static void kernel_stream_unlock(IOStream *s);
+
+/** Methods for a kernel stream. Output only. */
+static const IOMethods kernel_methods = {
+  write:  kernel_write,
+  free:   kernel_free,
+  lock:   kernel_stream_lock,
+  unlock: kernel_stream_unlock,
+};
+
+/** Shared state for kernel streams.
+ * All implementations write using printk, so we can use
+ * shared state and avoid allocating it.
+ */
+static const KernelData kernel_data = {
+  lock: SPIN_LOCK_UNLOCKED,
+  flags: 0,
+  buf_n: BUF_N,
+};
+
+/** Stream for kernel printk. */
+static IOStream iokernel = {
+    methods: &kernel_methods,
+    data:    &kernel_data,
+};
+
+/** Stream for kernel printk. */
+IOStream *iostdout = &iokernel;
+
+/** Stream for kernel printk. */
+IOStream *iostdin = &iokernel;
+
+/** Stream for kernel printk. */
+IOStream *iostderr = &iokernel;
+
+/** Get an output-only stream implementation using
+ * printk(). The stream uses static storage, and must not be freed.
+ *
+ * @return kernel stream
+ */
+IOStream get_stream_kernel(void){
+  return iokernel;
+}
+
+/** Obtain the lock on the stream state.
+ *
+ * @param kdata stream state
+ */
+static inline void KernelData_lock(KernelData *kdata){
+  spin_lock_irqsave(&kdata->lock, kdata->flags);
+}
+
+/** Release the lock on the stream state.
+ *
+ * @param kdata stream state
+ */
+static inline void KernelData_unlock(KernelData *kdata){
+  spin_unlock_irqrestore(&kdata->lock, kdata->flags);
+}
+
+/** Get the stream state.
+ *
+ * @param s kernel stream
+ * @return stream state
+ */
+static inline KernelData *get_kernel_data(IOStream *s){
+  return (KernelData*)s->data;
+}
+
+/** Obtain the lock on the stream state.
+ *
+ * @param s stream
+ */
+void kernel_stream_lock(IOStream *s){
+    KernelData_lock(get_kernel_data(s));
+}
+
+/** Release the lock on the stream state.
+ *
+ * @param s stream
+ */
+void kernel_stream_unlock(IOStream *s){
+    KernelData_unlock(get_kernel_data(s));
+}
+
+/** Write to a kernel stream.
+ *
+ * @param stream kernel stream
+ * @param format print format
+ * @param args print arguments
+ * @return result of the print
+ */
+static int kernel_write(IOStream *stream, const char *buf, int n){
+  KernelData *kdata = get_kernel_data(stream);
+  int k;
+  k = kdata->buf_n - 1;
+  if(n < k) k = n;
+  memcpy(kdata->buf, buf, k);
+  kdata->buf[k] = '\0'
+  printk(kdata->buf);
+  return k;
+}
+
+/** Free a kernel stream.
+ * Frees the internal state of the stream.
+ * Do not call this unless the stream was dynamically allocated.
+ * Do not call this on a stream returned from get_stream_kernel().
+ *
+ * @param io stream to free
+ */
+static void kernel_free(IOStream *io){
+  KernelData *kdata;
+  if(io == &iokernel) return;
+  kdata = get_kernel_data(io);
+  zero(kdata, sizeof(*kdata));
+  deallocate(kdata);
+}
+#endif /* __KERNEL__ */
+
+
+
+
diff --git a/tools/lib/kernel_stream.h b/tools/lib/kernel_stream.h
new file mode 100644 (file)
index 0000000..be370f2
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2001 - 2004 Mike Wray <mike.wray@hp.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef _XEN_LIB_KERNEL_STREAM_H_
+#define _XEN_LIB_KERNEL_STREAM_H_
+
+#ifdef __KERNEL__
+#include "iostream.h"
+
+extern IOStream get_stream_kernel(void);
+#define get_stream_stdout get_stream_kernel
+
+#endif /* __KERNEL__ */
+#endif /* !_XEN_LIB_KERNEL_STREAM_H_ */
diff --git a/tools/lib/lexis.c b/tools/lib/lexis.c
new file mode 100644 (file)
index 0000000..26d2ec4
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of the
+ * License, or  (at your option) any later version. This library is 
+ * distributed in the  hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+/** @file
+ * Lexical analysis.
+ */
+
+#include "sys_string.h"
+#include "lexis.h"
+#include <errno.h>
+
+/** Check if a value lies in a (closed) range.
+ *
+ * @param x value to test
+ * @param lo low end of the range
+ * @param hi high end of the range
+ * @return 1 if x is in the interval [lo, hi], 0 otherwise
+ */
+inline static int in_range(int x, int lo, int hi){
+    return (lo <= x) && (x <= hi);
+}
+
+/** Determine if a string is an (unsigned) decimal number.
+ * 
+ * @param s pointer to characters to test
+ * @param n length of string
+ * @return 1 if s is a decimal number, 0 otherwise.
+ */
+int is_decimal_number(const char *s, int n){
+    int i;
+    if(n <= 0)return 0;
+    for(i = 0; i < n; i++){
+        if(!in_decimal_digit_class(s[i])) return 0;
+    }
+    return 1;
+}
+
+/** Determine if a string is a hex number.
+ * Hex numbers are 0, or start with 0x or 0X followed
+ * by a non-zero number of hex digits (0-9,a-f,A-F).
+ * 
+ * @param s pointer to characters to test
+ * @param n length of string
+ * @return 1 if s is a hex number, 0 otherwise.
+ */
+int is_hex_number(const char *s, int n){
+    int i;
+    if(n <= 0) return 0;
+    if(n == 1){
+        return s[0]=='0';
+    }
+    if(n <= 3) return 0;
+    if(s[0] != '0' || (s[1] != 'x' && s[1] != 'X')) return 0;
+    for(i = 2; i < n; i++){
+        if(!in_hex_digit_class(s[i])) return 0;
+    }
+    return 1;
+}
+
+/** Test if a string matches a keyword.
+ * The comparison is case-insensitive.
+ * The comparison fails if either argument is null.
+ *
+ * @param s string
+ * @param k keyword
+ * @return 1 if they match, 0 otherwise
+ */
+int is_keyword(const char *s, const char *k){
+  return s && k && !strcasecmp(s, k);
+}
+
+/** Test if a string matches a character.
+ *
+ * @param s string
+ * @param c character (non-null)
+ * @return 1 if s contains exactly c, 0 otherwise
+ */
+int is_keychar(const char *s, char c){
+  return c && (s[0] == c) && !s[1];
+}
diff --git a/tools/lib/lexis.h b/tools/lib/lexis.h
new file mode 100644 (file)
index 0000000..7d8fe7b
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of the
+ * License, or  (at your option) any later version. This library is 
+ * distributed in the  hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#ifndef _SP_LEXIS_H_
+#define _SP_LEXIS_H_
+
+#include "sys_string.h"
+#include "sys_ctype.h"
+
+/** @file
+ * Lexical analysis.
+ */
+
+/** Class of characters treated as space. */
+#define space_class ((char []){ '\n', '\r', '\t', ' ', '\f' , 0 })
+
+/** Class of separator characters. */
+#define sep_class "{}()<>[]@!;"
+
+#define comment_class "#"
+
+/** Determine if a character is in a given class.
+ * 
+ * @param c character to test
+ * @param s null-terminated string of characters in the class
+ * @return 1 if c is in the class, 0 otherwise.
+ */
+static inline int in_class(int c, const char *s){
+  return s && (strchr(s, c) != 0);
+}
+
+/** Determine if a character is in the space class.
+ * 
+ * @param c character to test
+ * @return 1 if c is in the class, 0 otherwise.
+ */
+static inline int in_space_class(int c){
+    return in_class(c, space_class);
+}
+
+static inline int in_comment_class(int c){
+    return in_class(c, comment_class);
+}
+
+/** Determine if a character is in the separator class.
+ * Separator characters terminate tokens, and do not need space
+ * to separate them.
+ * 
+ * @param c character to test
+ * @return 1 if c is in the class, 0 otherwise.
+ */
+static inline int in_sep_class(int c){
+    return in_class(c, sep_class);
+}
+
+/** Determine if a character is in the alpha class.
+ * 
+ * @param c character to test
+ * @return 1 if c is in the class, 0 otherwise.
+ */
+static inline int in_alpha_class(int c){
+    return isalpha(c);
+}
+
+/** Determine if a character is in the octal digit class.
+ * 
+ * @param c character to test
+ * @return 1 if c is in the class, 0 otherwise.
+ */
+static inline int in_octal_digit_class(int c){
+    return '0' <= c && c <= '7';
+}
+
+/** Determine if a character is in the decimal digit class.
+ * 
+ * @param c character to test
+ * @return 1 if c is in the class, 0 otherwise.
+ */
+static inline int in_decimal_digit_class(int c){
+    return isdigit(c);
+}
+
+/** Determine if a character is in the hex digit class.
+ * 
+ * @param c character to test
+ * @return 1 if c is in the class, 0 otherwise.
+ */
+static inline int in_hex_digit_class(int c){
+    return isdigit(c) || in_class(c, "abcdefABCDEF");
+}
+
+
+static inline int in_string_quote_class(int c){
+    return in_class(c, "'\"");
+}
+
+static inline int in_printable_class(int c){
+    return ('A' <= c && c <= 'Z')
+        || ('a' <= c && c <= 'z')
+        || ('0' <= c && c <= '9')
+        || in_class(c, "!$%&*+,-./:;<=>?@^_`{|}~");
+}
+
+extern int is_decimal_number(const char *s, int n);
+extern int is_hex_number(const char *s, int n);
+extern int is_keyword(const char *s, const char *k);
+extern int is_keychar(const char *s, char c);
+
+#endif /* !_SP_LEXIS_H_ */
diff --git a/tools/lib/lzi_stream.c b/tools/lib/lzi_stream.c
new file mode 100644 (file)
index 0000000..0f09734
--- /dev/null
@@ -0,0 +1,590 @@
+/* $Id: lzi_stream.c,v 1.4 2003/09/30 15:22:53 mjw Exp $ */
+#define __FILE_ID_INFO "$Id: lzi_stream.c,v 1.4 2003/09/30 15:22:53 mjw Exp $"
+#include <what.h>
+static char __rcsid[] __attribute__((unused)) = WHAT_ID __FILE_ID_INFO;
+/*
+ * Copyright (C) 2003 Hewlett-Packard Company.
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/** @file
+ * An IOStream implementation using LZI to provide compression and decompression.
+ * This is designed to provide compression without output latency.
+ * Flushing an LZI stream flushes all pending data to the underlying stream.
+ * This is essential for stream-based (e.g. networked) applications.
+ *
+ * A compressed data stream is a sequence of blocks.
+ * Each block is the block size followed by the compressed data.
+ * The last block has size zero.
+ * Sizes are 4-byte unsigned in network order.
+ *
+ * This format allows compressed data to be read from a stream without reading
+ * past the logical end of compressed data.
+ *
+ * @author Mike Wray <mike.wray@hpl.hp.com>
+ */
+#ifndef __KERNEL__
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+
+#include "zlib.h"
+
+#include "allocate.h"
+#include "lzi_stream.h"
+#include "file_stream.h"
+#include "marshal.h"
+
+#define dprintf(fmt, args...) fprintf(stdout, "[DEBUG] LZI>%s" fmt, __FUNCTION__, ##args)
+#define wprintf(fmt, args...) fprintf(stderr, "[WARN]  LZI>%s" fmt, __FUNCTION__, ##args)
+#define iprintf(fmt, args...) fprintf(stdout, "[INFO]  LZI>%s" fmt, __FUNCTION__, ##args)
+#define eprintf(fmt, args...) fprintf(stderr, "[ERROR] LZI>%s" fmt, __FUNCTION__, ##args)
+
+static int lzi_read(IOStream *s, void *buf, size_t size, size_t count);
+static int lzi_write(IOStream *s, const void *buf, size_t size, size_t count);
+static int lzi_print(IOStream *s, const char *msg, va_list args);
+static int lzi_getc(IOStream *s);
+static int lzi_error(IOStream *s);
+static int lzi_close(IOStream *s);
+static void lzi_free(IOStream *s);
+static int lzi_flush(IOStream *s);
+
+enum {
+    LZI_WRITE = 1,
+    LZI_READ = 2,
+};
+
+/** Methods used by a gzFile* IOStream. */
+static const IOMethods lzi_methods = {
+    read: lzi_read,
+    write: lzi_write,
+    print: lzi_print,
+    getc:  lzi_getc,
+    error: lzi_error,
+    close: lzi_close,
+    free:  lzi_free,
+    flush: lzi_flush,
+};
+
+#define BUFFER_SIZE (512 * 1024)
+
+typedef struct LZIState {
+    z_stream zstream;
+    void *inbuf;
+    uint32_t inbuf_size;
+    void *outbuf;
+    uint32_t outbuf_size;
+    /** Underlying stream for I/O. */
+    IOStream *io;
+    /** Flags. */
+    int flags;
+    /** Error indicator. */
+    int error;
+    int eof;
+    int plain_bytes;
+    int comp_bytes;
+    int zstream_initialized;
+    int flushed;
+} LZIState;
+
+static inline int LZIState_writeable(LZIState *s){
+    return (s->flags & LZI_WRITE) != 0;
+}
+
+static inline int LZIState_readable(LZIState *s){
+    return (s->flags & LZI_READ) != 0;
+}
+
+void LZIState_free(LZIState *z){
+    if(!z) return;
+    if(z->zstream_initialized){
+        if(LZIState_writeable(z)){
+            deflateEnd(&z->zstream);
+        } else if(LZIState_readable(z)){
+            inflateEnd(&z->zstream);
+        }
+    }
+    deallocate(z->inbuf);
+    deallocate(z->outbuf);
+    deallocate(z);
+}
+
+static int mode_flags(const char *mode, int *flags){
+    int err = 0;
+    int r=0, w=0;
+    if(!mode){
+        err = -EINVAL;
+        goto exit;
+    }
+    for(; *mode; mode++){
+        if(*mode == 'w') w = 1;
+        if(*mode == 'r') r = 1;
+    }
+    if(r + w != 1){
+        err = -EINVAL;
+        goto exit;
+    }
+    if(r) *flags |= LZI_READ;
+    if(w) *flags |= LZI_WRITE;
+  exit:
+    return err;
+}
+
+/** Get the stream state.
+ * 
+ * @param s lzi stream
+ * @return stream state.
+ */
+static inline LZIState * lzi_state(IOStream *io){
+    return io->data;
+}
+
+IOStream *lzi_stream_io(IOStream *io){
+    LZIState *s = lzi_state(io);
+    return s->io;
+}
+
+static inline void set_error(LZIState *s, int err){
+    if(err < 0 && !s->error){
+        s->error = err;
+    }
+}
+
+static int zerror(LZIState *s, int err){
+    if(err){
+        //dprintf("> err=%d\n", err);
+        if(err < 0) set_error(s, -EIO);
+    }
+    return s->error;
+}
+
+int lzi_stream_plain_bytes(IOStream *io){
+    LZIState *s = lzi_state(io);
+    return s->plain_bytes;
+}
+
+int lzi_stream_comp_bytes(IOStream *io){
+    LZIState *s = lzi_state(io);
+    return s->comp_bytes;
+}
+
+float lzi_stream_ratio(IOStream *io){
+    LZIState *s = lzi_state(io);
+    float ratio = 0.0;
+    if(s->comp_bytes){
+        ratio = ((float) s->comp_bytes)/((float) s->plain_bytes);
+    }
+    return ratio;
+}
+
+static int alloc(void **p, int n){
+    *p = allocate(n);
+    return (p ? 0 : -ENOMEM);
+}
+
+LZIState * LZIState_new(IOStream *io, int flags){
+    int err = -ENOMEM;
+    int zlevel = Z_BEST_SPEED; // Level 1 compression - fastest.
+    int zstrategy = Z_DEFAULT_STRATEGY;
+    int zwindow = MAX_WBITS;
+    int zmemory = 8;
+    LZIState *z = ALLOCATE(LZIState);
+
+    //dprintf(">\n");
+    if(!z) goto exit;
+    z->io = io;
+    z->flags = flags;
+
+    if(LZIState_writeable(z)){
+        z->outbuf_size = BUFFER_SIZE;
+        /* windowBits is passed < 0 to suppress zlib header */
+        err = deflateInit2(&z->zstream, zlevel, Z_DEFLATED, -zwindow, zmemory, zstrategy);
+        if (err != Z_OK) goto exit;
+        z->zstream_initialized = 1;
+        err = alloc(&z->outbuf, z->outbuf_size);
+        if(err) goto exit;
+        z->zstream.next_out = z->outbuf;
+        z->zstream.avail_out = z->outbuf_size;
+    } else {
+        z->inbuf_size = BUFFER_SIZE;
+        err = alloc(&z->inbuf, z->inbuf_size);
+        if(err) goto exit;
+        ///z->zstream.next_in  = z->inbuf;
+
+        /* windowBits is passed < 0 to tell that there is no zlib header.
+         * Note that in this case inflate *requires* an extra "dummy" byte
+         * after the compressed stream in order to complete decompression and
+         * return Z_STREAM_END. Here the gzip CRC32 ensures that 4 bytes are
+         * present after the compressed stream.
+         */
+        err = inflateInit2(&z->zstream, -zwindow);
+        if(err != Z_OK) goto exit;
+        z->zstream_initialized = 1;
+    }
+        
+  exit:
+    if(err){
+        LZIState_free(z);
+        z = NULL;
+    }
+    //dprintf("< z=%p\n", z);
+    return z;
+}
+
+int read_block(LZIState *s){
+    int err = 0, k = 0;
+    //dprintf(">\n");
+    if(s->eof) goto exit;
+    err = unmarshal_uint32(s->io, &k);
+    if(err) goto exit;
+    if(k > s->inbuf_size){
+        err = -EINVAL;
+        goto exit;
+    }
+    if(k){
+        err = unmarshal_bytes(s->io, s->inbuf, k);
+        if(err) goto exit;
+    } else {
+        s->eof = 1;
+    }        
+    s->zstream.avail_in = k;
+    s->zstream.next_in = s->inbuf;
+    s->comp_bytes += 4;
+    s->comp_bytes += k;
+  exit:
+    //dprintf("< err=%d\n", err);
+    return err;
+}
+
+int write_block(LZIState *s){
+    int err = 0;
+    int k = ((char*)s->zstream.next_out) - ((char*)s->outbuf);
+    int k2 = s->outbuf_size - s->zstream.avail_out;
+    //dprintf("> k=%d k2=%d\n", k, k2);
+    if(!k) goto exit;
+    err = marshal_uint32(s->io, k);
+    if(err) goto exit;
+    err = marshal_bytes(s->io, s->outbuf, k);
+    if(err) goto exit;
+    s->zstream.next_out = s->outbuf;
+    s->zstream.avail_out = s->outbuf_size;
+    s->comp_bytes += 4;
+    s->comp_bytes += k;
+  exit:
+    //dprintf("< err=%d\n", err);
+    return err;
+}
+
+int write_terminator(LZIState *s){
+    int err = 0;
+    char c = 0;
+    err = marshal_uint32(s->io, 1);
+    if(err) goto exit;
+    err = marshal_bytes(s->io, &c, 1);
+    if(err) goto exit;
+    err = marshal_uint32(s->io, 0);
+    if(err) goto exit;
+    s->comp_bytes += 9;
+  exit:
+    return err;
+}
+
+/** Write to the underlying stream using fwrite();
+ *
+ * @param io destination
+ * @param buf data
+ * @param size size of data elements
+ * @param count number of data elements to write
+ * @return number of data elements written
+ */
+static int lzi_write(IOStream *io, const void *buf, size_t size, size_t count){
+    int err = 0;
+    int n = size * count;
+    LZIState *s = lzi_state(io);
+
+    //dprintf("> buf=%p size=%d count=%d n=%d\n", buf, size, count, n);
+    if(!LZIState_writeable(s)){
+        err = -EINVAL;
+        goto exit;
+    }
+    s->flushed = 0;
+    s->zstream.next_in = (void*)buf;
+    s->zstream.avail_in = n;
+    while(s->zstream.avail_in){
+        if(s->zstream.avail_out == 0){
+            err = write_block(s);
+            if(err) goto exit;
+        }
+        //dprintf("> 1 deflate avail_in=%d avail_out=%d\n", s->zstream.avail_in, s->zstream.avail_out);
+        //dprintf("> 1 deflate next_in=%p next_out=%p\n", s->zstream.next_in, s->zstream.next_out);
+        err = zerror(s, deflate(&s->zstream, Z_NO_FLUSH));
+        //dprintf("> 2 deflate avail_in=%d avail_out=%d\n", s->zstream.avail_in, s->zstream.avail_out);
+        //dprintf("> 2 deflate next_in=%p next_out=%p\n", s->zstream.next_in, s->zstream.next_out);
+        if(err) goto exit;
+    }
+    err = n;
+    s->plain_bytes += n;
+    if(size != 1) err /= size;
+  exit:
+    //dprintf("< err=%d\n", err);
+    return err;
+}
+
+
+/** Read from the underlying stream.
+ *
+ * @param io input
+ * @param buf where to put input
+ * @param size size of data elements
+ * @param count number of data elements to read
+ * @return number of data elements read
+ */
+static int lzi_read(IOStream *io, void *buf, size_t size, size_t count){
+    int err, zerr;
+    int n = size * count;
+    LZIState *s = lzi_state(io);
+
+    //dprintf("> size=%d count=%d n=%d\n", size, count, n);
+    if(!LZIState_readable(s)){
+        err = -EINVAL;
+        goto exit;
+    }
+    s->zstream.next_out = buf;
+    s->zstream.avail_out = n;
+    while(s->zstream.avail_out){
+        if(s->zstream.avail_in == 0){
+            err = read_block(s);
+        }
+        //dprintf("> 1 deflate avail_in=%d avail_out=%d\n", s->zstream.avail_in, s->zstream.avail_out);
+        zerr = inflate(&s->zstream, Z_NO_FLUSH);
+        //dprintf("> 2 deflate avail_in=%d avail_out=%d\n", s->zstream.avail_in, s->zstream.avail_out);
+        if(zerr == Z_STREAM_END) break;
+        //dprintf("> zerr=%d\n", zerr);
+        err = zerror(s, zerr);
+        if(err) goto exit;
+    }
+    err = n - s->zstream.avail_out;
+    s->plain_bytes += err;
+    if(size != 1) err /= size;
+  exit:
+    set_error(s, err);
+    //dprintf("< err=%d\n", err);
+    return err;
+}
+
+/** Print to the underlying stream.
+ * Returns 0 if the formatted output is too big for the internal buffer.
+ *
+ * @param io lzi stream
+ * @param msg format to use
+ * @param args arguments
+ * @return result of the print
+ */
+static int lzi_print(IOStream *io, const char *msg, va_list args){
+    char buf[1024];
+    int buf_n = sizeof(buf);
+    int n;
+    LZIState *s = lzi_state(io);
+    if(!LZIState_writeable(s)){
+        n = -EINVAL;
+        goto exit;
+    }
+    n = vsnprintf(buf, buf_n, (char*)msg, args);
+    if(n < 0) goto exit;
+    if(n > buf_n){
+        n = 0;
+    } else {
+        n = lzi_write(io, buf, 1, n);
+    }
+  exit:
+    return n;
+}
+
+/** Read a character from the underlying stream
+ *
+ * @param io lzi stream
+ * @return character read, IOSTREAM_EOF on end of file (or error)
+ */
+static int lzi_getc(IOStream *io){
+    int err;
+    char c;
+    err = lzi_read(io, &c, 1, 1);
+    if(err < 1) c = EOF;
+    err = (c==EOF ? IOSTREAM_EOF : c);
+    return err;
+}
+
+static int flush_output(LZIState *s, int mode){
+    int err = 0, zerr;
+    int done = 0;
+    int avail_out_old;
+    int count = 10;
+
+    //dprintf("> avail_in=%d avail_out=%d\n", s->zstream.avail_in, s->zstream.avail_out);
+    if(s->flushed == 1 + mode) goto exit;
+    //s->zstream.avail_in = 0; /* should be zero already anyway */
+    for(;;){
+        // Write any available output.
+        if(done || s->zstream.avail_out == 0){
+            err = write_block(s);
+            if(err) goto exit;
+            if(done) break;
+        }
+        //dprintf("> 1 deflate avail_in=%d avail_out=%d\n", s->zstream.avail_in, s->zstream.avail_out);
+        avail_out_old = s->zstream.avail_out;
+        zerr = deflate(&s->zstream, mode);
+        err = zerror(s, zerr);
+        //dprintf("> 2 deflate avail_in=%d avail_out=%d\n", s->zstream.avail_in, s->zstream.avail_out);
+        //dprintf("> deflate=%d\n", err);
+        //done = (s->zstream.avail_out != 0);
+        //done = (s->zstream.avail_in == 0) && (s->zstream.avail_out == avail_out_old);
+        if(0 && mode == Z_FINISH){
+            done = (zerr ==  Z_STREAM_END);
+        } else {
+            done = (s->zstream.avail_in == 0)
+                //&& (s->zstream.avail_out == avail_out_old)
+                && (s->zstream.avail_out != 0);
+        }
+    }
+    s->flushed = 1 + mode;
+  exit:
+    //dprintf("< err=%d\n", err);
+    return err;
+}
+
+/** Flush any pending input to the underlying stream.
+ *
+ * @param s lzi stream
+ * @return 0 on success, error code otherwise
+ */
+static int lzi_flush(IOStream *io){
+    int err = 0;
+    LZIState *s = lzi_state(io);
+    //dprintf(">\n");
+    if(!LZIState_writeable(s)){
+        err = -EINVAL;
+        goto exit;
+    }
+    err = flush_output(s, Z_SYNC_FLUSH);
+    if(err) goto exit;
+    err = IOStream_flush(s->io);
+  exit:
+    set_error(s, err);
+    //dprintf("< err=%d\n", err);
+    return (err < 0 ? err : 0);
+}
+
+/** Check if a stream has an error.
+ *
+ * @param s lzi stream
+ * @return code if has an error, 0 otherwise
+ */
+static int lzi_error(IOStream *s){
+    int err = 0;
+    LZIState *state = lzi_state(s);
+    err = state->error;
+    if(err) goto exit;
+    err = IOStream_error(state->io);
+  exit:
+    return err;
+}
+
+/** Close an lzi stream.
+ *
+ * @param s lzi stream to close
+ * @return result of the close
+ */
+static int lzi_close(IOStream *io){
+    int err = 0;
+    LZIState *s = lzi_state(io);
+    if(LZIState_writeable(s)){
+        err = flush_output(s, Z_FINISH);
+        if(err) goto exit;
+        err = write_terminator(s);
+        if(err) goto exit;
+        err = IOStream_flush(s->io);
+    }   
+  exit:
+    err = IOStream_close(s->io);
+    set_error(s, err);
+    return err;
+}
+
+/** Free an lzi stream.
+ *
+ * @param s lzi stream
+ */
+static void lzi_free(IOStream *s){
+    LZIState *state = lzi_state(s);
+    IOStream_free(state->io);
+    LZIState_free(state);
+    s->data = NULL;
+}
+
+/** Create an lzi stream for an IOStream.
+ *
+ * @param io stream to wrap
+ * @return new IOStream using f for i/o
+ */
+IOStream *lzi_stream_new(IOStream *io, const char *mode){
+    int err = -ENOMEM;
+    int flags = 0;
+    IOStream *zio = NULL;
+    LZIState *state = NULL;
+
+    zio = ALLOCATE(IOStream);
+    if(!zio) goto exit;
+    err = mode_flags(mode, &flags);
+    if(err) goto exit;
+    state = LZIState_new(io, flags);
+    if(!state) goto exit;
+    err = 0;
+    zio->data = state;
+    zio->methods = &lzi_methods;
+  exit:
+    if(err){
+        if(state) LZIState_free(state);
+        if(zio) deallocate(zio);
+        zio = NULL;
+    }
+    return zio;
+}
+
+/** IOStream version of fdopen().
+ *
+ * @param fd file descriptor
+ * @param flags giving the mode to open in (as for fdopen())
+ * @return new stream for the open file, or NULL if failed
+ */
+IOStream *lzi_stream_fdopen(int fd, const char *mode){
+    int err = -ENOMEM;
+    IOStream *io = NULL, *zio = NULL;
+    io = file_stream_fdopen(fd, mode);
+    if(!io) goto exit;
+    zio = lzi_stream_new(io, mode);
+    if(!io) goto exit;
+    err = 0;
+  exit:
+    if(err){
+        IOStream_free(io);
+        IOStream_free(zio);
+        zio = NULL;
+    }
+    return zio;
+}
+#endif
diff --git a/tools/lib/lzi_stream.h b/tools/lib/lzi_stream.h
new file mode 100644 (file)
index 0000000..0ad4f8d
--- /dev/null
@@ -0,0 +1,36 @@
+#/* $Id: lzi_stream.h,v 1.3 2003/09/30 15:22:53 mjw Exp $ */
+/*
+ * Copyright (C) 2003 Hewlett-Packard Company.
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef _SP_LZI_STREAM_H_
+#define _SP_LZI_STREAM_H_
+
+#ifndef __KERNEL__
+#include "iostream.h"
+
+extern IOStream *lzi_stream_new(IOStream *io, const char *mode);
+extern IOStream *lzi_stream_fopen(const char *file, const char *mode);
+extern IOStream *lzi_stream_fdopen(int fd, const char *mode);
+extern IOStream *lzi_stream_io(IOStream *zio);
+
+extern int lzi_stream_plain_bytes(IOStream *io);
+extern int lzi_stream_comp_bytes(IOStream *io);
+extern float lzi_stream_ratio(IOStream *io);
+
+#endif
+#endif /* !_SP_FILE_STREAM_H_ */
diff --git a/tools/lib/lzo_stream.c b/tools/lib/lzo_stream.c
new file mode 100644 (file)
index 0000000..bf7c348
--- /dev/null
@@ -0,0 +1,596 @@
+/* $Id: lzo_stream.c,v 1.4 2003/09/30 15:22:53 mjw Exp $ */
+#define __FILE_ID_INFO "$Id: lzo_stream.c,v 1.4 2003/09/30 15:22:53 mjw Exp $"
+#include <what.h>
+static char __rcsid[] __attribute__((unused)) = WHAT_ID __FILE_ID_INFO;
+/*
+ * Copyright (C) 2003 Hewlett-Packard Company.
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/** @file
+ * An IOStream implementation using LZO to provide compression and decompression.
+ * This is designed to provide reasonable compression without output latency.
+ * Flushing an LZO stream flushes all pending data to the underlying stream.
+ * This is essential for stream-based (e.g. networked) applications.
+ *
+ * A compressed data stream is a sequence of blocks.
+ * Each block except the last is the plain data size followed by the compressed data size
+ * and the compressed data. The last block has plain data size zero and omits the rest.
+ * Sizes are 4-byte unsigned in network order. If the compressed size is smaller than
+ * the plain size the block data is compressed, otherwise it is plain (uncompressed).
+ *
+ * This format allows compressed data to be read from a stream without reading
+ * past the logical end of compressed data.
+ *
+ * @author Mike Wray <mike.wray@hpl.hp.com>
+ */
+#ifndef __KERNEL__
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+
+#include "lzo1x.h"
+
+#include "allocate.h"
+#include "lzo_stream.h"
+#include "file_stream.h"
+#include "marshal.h"
+
+#define dprintf(fmt, args...) fprintf(stdout, "[DEBUG] LZO>%s" fmt, __FUNCTION__, ##args)
+#define wprintf(fmt, args...) fprintf(stderr, "[WARN]  LZO>%s" fmt, __FUNCTION__, ##args)
+#define iprintf(fmt, args...) fprintf(stdout, "[INFO]  LZO>%s" fmt, __FUNCTION__, ##args)
+#define eprintf(fmt, args...) fprintf(stderr, "[ERROR] LZO>%s" fmt, __FUNCTION__, ##args)
+
+static int lzo_read(IOStream *s, void *buf, size_t size, size_t count);
+static int lzo_write(IOStream *s, const void *buf, size_t size, size_t count);
+static int lzo_print(IOStream *s, const char *msg, va_list args);
+static int lzo_getc(IOStream *s);
+static int lzo_error(IOStream *s);
+static int lzo_close(IOStream *s);
+static void lzo_free(IOStream *s);
+static int lzo_flush(IOStream *s);
+
+enum {
+    LZO_WRITE = 1,
+    LZO_READ = 2,
+};
+
+/** Methods used by a gzFile* IOStream. */
+static const IOMethods lzo_methods = {
+    read: lzo_read,
+    write: lzo_write,
+    print: lzo_print,
+    getc:  lzo_getc,
+    error: lzo_error,
+    close: lzo_close,
+    free:  lzo_free,
+    flush: lzo_flush,
+};
+
+//#define PLAIN_SIZE (64 * 1024)
+//#define PLAIN_SIZE (128 * 1024)
+#define PLAIN_SIZE (512 * 1024)
+
+//#define NOCOMPRESS
+
+typedef struct LZOState {
+    /** Flags. */
+    int flags;
+    /** Error indicator. */
+    int error;
+    /** Underlying stream for I/O. */
+    IOStream *io;
+    /** Working memory (only needed for compression, not decompression). */
+    lzo_byte *memory;
+    /** Buffer for plain (uncompressed) data. */
+    lzo_byte *plain;
+    /** Size of the plain buffer. */
+    lzo_uint plain_size;
+    /** Pointer into the plain buffer. */
+    lzo_byte *plain_ptr;
+    /** Number of bytes of plain data available. */
+    lzo_uint plain_n;
+    /** Buffer for compressed data. */
+    lzo_byte *comp;
+    /** Size of the compressed buffer. */
+    lzo_uint comp_size;
+
+    int plain_bytes;
+    int comp_bytes;
+} LZOState;
+
+void LZOState_free(LZOState *z){
+    if(!z) return;
+    deallocate(z->memory);
+    deallocate(z->plain);
+    deallocate(z->comp);
+    deallocate(z);
+}
+
+/** Maximum size of compressed data for the given plain data size.
+ *
+ * @param plain_size size of plain data
+ * @return maximum size of compressed data
+ */
+static int comp_size(int plain_size){
+    return plain_size + (plain_size / 64) + 16 + 3;
+}
+
+static int mode_flags(const char *mode, int *flags){
+    int err = 0;
+    int r=0, w=0;
+    if(!mode){
+        err = -EINVAL;
+        goto exit;
+    }
+    for(; *mode; mode++){
+        if(*mode == 'w') w = 1;
+        if(*mode == 'r') r = 1;
+    }
+    if(r + w != 1){
+        err = -EINVAL;
+        goto exit;
+    }
+    if(r) *flags |= LZO_READ;
+    if(w) *flags |= LZO_WRITE;
+  exit:
+    return err;
+}
+
+/** Get the stream state.
+ * 
+ * @param s lzo stream
+ * @return stream state.
+ */
+static inline LZOState * lzo_state(IOStream *s){
+    return s->data;
+}
+
+IOStream *lzo_stream_io(IOStream *s){
+    LZOState *state = lzo_state(s);
+    return state->io;
+}
+
+static inline void set_error(LZOState *state, int err){
+    if(err < 0 && !state->error){
+        state->error = err;
+    }
+}
+
+int lzo_stream_plain_bytes(IOStream *s){
+    LZOState *state = lzo_state(s);
+    return state->plain_bytes;
+}
+
+int lzo_stream_comp_bytes(IOStream *s){
+    LZOState *state = lzo_state(s);
+    return state->comp_bytes;
+}
+
+float lzo_stream_ratio(IOStream *s){
+    LZOState *state = lzo_state(s);
+    float ratio = 0.0;
+    if(state->comp_bytes){
+        ratio = ((float) state->comp_bytes)/((float) state->plain_bytes);
+    }
+    return ratio;
+}
+
+static inline int LZOState_writeable(LZOState *state){
+    return (state->flags & LZO_WRITE) != 0;
+}
+
+static inline int LZOState_readable(LZOState *state){
+    return (state->flags & LZO_READ) != 0;
+}
+
+LZOState * LZOState_new(IOStream *io, int flags){
+    int err = -ENOMEM;
+    LZOState *z = ALLOCATE(LZOState);
+    //dprintf(">\n");
+    if(!z) goto exit;
+    z->io = io;
+    z->flags = flags;
+    if(LZOState_writeable(z)){
+        z->memory = allocate(LZO1X_1_MEM_COMPRESS);
+        if(!z->memory) goto exit;
+    }
+    z->plain_size = PLAIN_SIZE;
+    z->plain = allocate(z->plain_size);
+    if(!z->plain) goto exit;
+    z->plain_ptr = z->plain;
+    z->comp_size = comp_size(z->plain_size);
+    z->comp = allocate(z->comp_size);
+    if(!z->comp) goto exit;
+    err = 0;
+  exit:
+    if(err){
+        LZOState_free(z);
+        z = NULL;
+    }
+    //dprintf("< z=%p\n", z);
+    return z;
+}
+
+static int lzo_compress(LZOState *state){
+    int err = 0;
+    int k, comp_n;
+    //dprintf(">\n");
+    //dprintf(">plain=%p plain_n=%d comp=%p memory=%p\n", state->plain, state->plain_n, state->comp, state->memory);
+    // Compress the plain buffer.
+    err = lzo1x_1_compress(state->plain, state->plain_n,
+                           state->comp, &comp_n,
+                           state->memory);
+    //dprintf("> err=%d plain_n=%d comp_n=%d\n", err, state->plain_n, comp_n);
+    // Write plain size, compressed size.
+    err = marshal_uint32(state->io, state->plain_n);
+    if(err) goto exit;
+    err = marshal_uint32(state->io, comp_n);
+    if(err) goto exit;
+    //dprintf("> write data...\n");
+    // Write the smaller of the compressed and plain data.
+    if(state->plain_n < comp_n){
+        k = state->plain_n;
+        err = marshal_bytes(state->io, state->plain, state->plain_n);
+    } else {
+        k = comp_n;
+        err = marshal_bytes(state->io, state->comp, comp_n);
+    }
+    if(err) goto exit;
+    // Total output bytes.
+    k+= 8;
+    //dprintf("> wrote %d bytes\n", k);
+    state->plain_bytes += state->plain_n;
+    state->comp_bytes += k;
+    //dprintf("> plain=%d, comp=%d, ratio=%3.2f\n",
+    //        state->plain_bytes, state->comp_bytes,
+    //        ((float)state->comp_bytes)/((float)state->plain_bytes));
+    // Reset the plain buffer.
+    state->plain_ptr = state->plain;
+    state->plain_n = 0;
+    err = k;
+  exit:
+    //dprintf("< err=%d\n", err);
+    return err;
+}
+
+static int lzo_decompress(LZOState *state){
+    int plain_n, comp_n;
+    int err, k;
+    //dprintf(">\n");
+    err = unmarshal_uint32(state->io, &plain_n);
+    //dprintf("> err=%d plain_n=%d\n", err, plain_n);
+    if(err) goto exit;
+    state->comp_bytes += 4;
+    if(plain_n == 0) goto exit;
+    err = unmarshal_uint32(state->io, &comp_n);
+    //dprintf("> err=%d comp_n=%d\n", err, comp_n);
+    if(err) goto exit;
+    state->comp_bytes += 4;
+    if(plain_n > state->plain_size){
+        err = -EINVAL;
+        goto exit;
+    }
+    if(comp_n > plain_n){
+        //dprintf("> reading plain data %d...\n", plain_n);
+        k = plain_n;
+        err = unmarshal_bytes(state->io, state->plain, plain_n);
+        state->plain_n = plain_n;
+    } else {
+        //dprintf("> reading comp data %d...\n", comp_n);
+        k = comp_n;
+        err = unmarshal_bytes(state->io, state->comp, comp_n);
+        //dprintf("> decompress comp_n=%d\n", comp_n);
+        err = lzo1x_decompress(state->comp, comp_n,
+                               state->plain, &state->plain_n,
+                               state->memory);
+        //dprintf("> err=%d plain=%d state->plain_n=%d\n", err, plain_n, state->plain_n);
+        if(err != LZO_E_OK || state->plain_n != plain_n){
+            // Bad. Corrupted input.
+            err = -EINVAL;
+            eprintf("> Corrupted!\n");
+            goto exit;
+        }
+    }
+    state->comp_bytes += k;
+    state->plain_bytes += state->plain_n;
+    state->plain_ptr = state->plain;
+    err = k;
+  exit:
+    //dprintf("< err=%d\n", err);
+    return err;
+}
+
+/** Write to the underlying stream using fwrite();
+ *
+ * @param stream destination
+ * @param buf data
+ * @param size size of data elements
+ * @param count number of data elements to write
+ * @return number of data elements written
+ */
+static int lzo_write(IOStream *s, const void *buf, size_t size, size_t count){
+    int err = 0;
+    int n = size * count; // Total number of bytes to write.
+    int chunk;            // Size of chunk to write.
+    int remaining;        // Number of bytes remaining to write.
+    int space;            // Amount of space left in plain buffer.
+    LZOState *state = lzo_state(s);
+#ifdef NOCOMPRESS
+    //dprintf("> buf=%p size=%d count=%d\n", buf, size, count);
+    err = IOStream_write(state->io, buf, size, count);
+    //dprintf("< err=%d\n", err);
+#else
+    //dprintf("> buf=%p size=%d count=%d n=%d\n", buf, size, count, n);
+    remaining = n;
+    space = state->plain_size - state->plain_n;
+    //dprintf("> plain=%p plain_ptr=%p plain_n=%d space=%d\n",
+    //        state->plain, state->plain_ptr, state->plain_n, space);
+    while(remaining){
+        chunk = remaining;
+        if(chunk > space) chunk = space;
+        //dprintf("> memcpy %p %p %d\n", state->plain_ptr, buf, chunk);
+        memcpy(state->plain_ptr, buf, chunk);
+        remaining -= chunk;
+        space -= chunk;
+        state->plain_ptr += chunk;
+        state->plain_n += chunk;
+        if(space == 0){
+            // Input buffer is full. Compress and write it.
+            err = lzo_compress(state);
+            if(err < 0) goto exit;
+            space = state->plain_size - state->plain_n;
+        }
+    }
+    err = (size > 1 ? n / size : n);
+  exit:
+    set_error(state, err);
+#endif
+    return err;
+}
+
+
+/** Read from the underlying stream.
+ *
+ * @param stream input
+ * @param buf where to put input
+ * @param size size of data elements
+ * @param count number of data elements to read
+ * @return number of data elements read
+ */
+static int lzo_read(IOStream *s, void *buf, size_t size, size_t count){
+    int err = 0;
+    int k = 0;                     // Number of (plain) bytes read.
+    int remaining = size * count;  // Number of bytes remaining to read.
+    int chunk;                     // Size of chunk to read.
+    LZOState *state = lzo_state(s);
+#ifdef NOCOMPRESS
+    //dprintf("> buf=%p size=%d count=%d\n", buf, size, count);
+    err = IOStream_read(state->io, buf, size, count);
+    //dprintf("< err=%d\n", err);
+#else
+    if(!(state->flags & LZO_READ)){
+        err = -EINVAL;
+        goto exit;
+    }
+    while(remaining){
+        if(state->plain_n == 0){
+            // No more plain input, decompress some more.
+            err = lzo_decompress(state);
+            if(err < 0) goto exit;
+            // Stop reading if there is no more input.
+            if(err == 0 || state->plain_n == 0) break;
+        }
+        chunk = remaining;
+        if(chunk > state->plain_n) chunk = state->plain_n;
+        memcpy(buf, state->plain_ptr, chunk);
+        k += chunk;
+        buf += chunk;
+        state->plain_ptr += chunk;
+        state->plain_n -= chunk;
+        remaining -= chunk;
+    }
+    err = k;
+  exit:
+    set_error(state, err);
+#endif
+    return err;
+}
+
+/** Print to the underlying stream.
+ * Returns 0 if the formatted output is too big for the internal buffer.
+ *
+ * @param s lzo stream
+ * @param msg format to use
+ * @param args arguments
+ * @return result of the print
+ */
+static int lzo_print(IOStream *s, const char *msg, va_list args){
+    char buf[1024];
+    int buf_n = sizeof(buf);
+    int n;
+    LZOState *state = lzo_state(s);
+    if(!LZOState_writeable(state)){
+        n = -EINVAL;
+        goto exit;
+    }
+    n = vsnprintf(buf, buf_n, (char*)msg, args);
+    if(n < 0) goto exit;
+    if(n > buf_n){
+        n = 0;
+    } else {
+        n = lzo_write(s, buf, 1, n);
+    }
+  exit:
+    return n;
+}
+
+/** Read a character from the underlying stream
+ *
+ * @param s lzo stream
+ * @return character read, IOSTREAM_EOF on end of file (or error)
+ */
+static int lzo_getc(IOStream *s){
+    int err;
+    char c;
+    err = lzo_read(s, &c, 1, 1);
+    if(err < 1) c = EOF;
+    err = (c==EOF ? IOSTREAM_EOF : c);
+    return err;
+}
+
+/** Flush any pending input to the underlying stream.
+ *
+ * @param s lzo stream
+ * @return 0 on success, error code otherwise
+ */
+static int lzo_flush(IOStream *s){
+    int err = 0;
+    LZOState *state = lzo_state(s);
+    //dprintf(">\n");
+#ifdef NOCOMPRESS
+    err = IOStream_flush(state->io);
+#else    
+    if(!LZOState_writeable(state)){
+        err = -EINVAL;
+        goto exit;
+    }
+    if(state->plain_n){
+        err = lzo_compress(state);
+        if(err < 0) goto exit;
+    }
+    err = IOStream_flush(state->io);
+  exit:
+    set_error(state, err);
+#endif
+    //dprintf("< err=%d\n", err);
+    return (err < 0 ? err : 0);
+}
+
+/** Check if a stream has an error.
+ *
+ * @param s lzo stream
+ * @return code if has an error, 0 otherwise
+ */
+static int lzo_error(IOStream *s){
+    int err = 0;
+    LZOState *state = lzo_state(s);
+    err = state->error;
+    if(err) goto exit;
+    err = IOStream_error(state->io);
+  exit:
+    return err;
+}
+
+int lzo_stream_finish(IOStream *s){
+    int err = 0;
+    LZOState *state = lzo_state(s);
+    if(!LZOState_writeable(state)){
+        err = -EINVAL;
+        goto exit;
+    }
+    err = lzo_flush(s);
+    if(err < 0) goto exit;
+    err = marshal_int32(state->io, 0);
+  exit:
+    return err;
+}        
+
+/** Close an lzo stream.
+ *
+ * @param s lzo stream to close
+ * @return result of the close
+ */
+static int lzo_close(IOStream *s){
+    int err = 0;
+    LZOState *state = lzo_state(s);
+#ifdef NOCOMPRESS
+    err = IOStream_close(state->io);
+#else    
+    if(LZOState_writeable(state)){
+        err = lzo_stream_finish(s);
+    }        
+    err = IOStream_close(state->io);
+    set_error(state, err);
+#endif
+    return err;
+}
+
+/** Free an lzo stream.
+ *
+ * @param s lzo stream
+ */
+static void lzo_free(IOStream *s){
+    LZOState *state = lzo_state(s);
+    IOStream_free(state->io);
+    LZOState_free(state);
+    s->data = NULL;
+}
+
+/** Create an lzo stream for an IOStream.
+ *
+ * @param io stream to wrap
+ * @return new IOStream using f for i/o
+ */
+IOStream *lzo_stream_new(IOStream *io, const char *mode){
+    int err = -ENOMEM;
+    int flags = 0;
+    IOStream *zio = NULL;
+    LZOState *state = NULL;
+
+    zio = ALLOCATE(IOStream);
+    if(!zio) goto exit;
+    err = mode_flags(mode, &flags);
+    if(err) goto exit;
+    state = LZOState_new(io, flags);
+    if(!state) goto exit;
+    err = 0;
+    zio->data = state;
+    zio->methods = &lzo_methods;
+  exit:
+    if(err){
+        if(state) LZOState_free(state);
+        if(zio) deallocate(zio);
+        zio = NULL;
+    }
+    return zio;
+}
+
+/** IOStream version of fdopen().
+ *
+ * @param fd file descriptor
+ * @param flags giving the mode to open in (as for fdopen())
+ * @return new stream for the open file, or NULL if failed
+ */
+IOStream *lzo_stream_fdopen(int fd, const char *mode){
+    int err = -ENOMEM;
+    IOStream *io = NULL, *zio = NULL;
+    io = file_stream_fdopen(fd, mode);
+    if(!io) goto exit;
+    zio = lzo_stream_new(io, mode);
+    if(!io) goto exit;
+    err = 0;
+  exit:
+    if(err){
+        IOStream_free(io);
+        IOStream_free(zio);
+        zio = NULL;
+    }
+    return zio;
+}
+#endif
diff --git a/tools/lib/lzo_stream.h b/tools/lib/lzo_stream.h
new file mode 100644 (file)
index 0000000..493da7b
--- /dev/null
@@ -0,0 +1,36 @@
+#/* $Id: lzo_stream.h,v 1.3 2003/09/30 15:22:53 mjw Exp $ */
+/*
+ * Copyright (C) 2003 Hewlett-Packard Company.
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef _SP_LZO_STREAM_H_
+#define _SP_LZO_STREAM_H_
+
+#ifndef __KERNEL__
+#include "iostream.h"
+
+extern IOStream *lzo_stream_new(IOStream *io, const char *mode);
+extern IOStream *lzo_stream_fopen(const char *file, const char *mode);
+extern IOStream *lzo_stream_fdopen(int fd, const char *mode);
+extern IOStream *lzo_stream_io(IOStream *zio);
+
+extern int lzo_stream_plain_bytes(IOStream *io);
+extern int lzo_stream_comp_bytes(IOStream *io);
+extern float lzo_stream_ratio(IOStream *io);
+
+#endif
+#endif /* !_SP_FILE_STREAM_H_ */
diff --git a/tools/lib/marshal.c b/tools/lib/marshal.c
new file mode 100644 (file)
index 0000000..21691d4
--- /dev/null
@@ -0,0 +1,207 @@
+#include <errno.h>
+#include "sys_net.h"
+#include "allocate.h"
+#include "marshal.h"
+
+#define dprintf(fmt, args...) IOStream_print(iostdout, "[DEBUG] %s" fmt, __FUNCTION__, ##args)
+#define wprintf(fmt, args...) IOStream_print(iostderr, "[WARN]  %s" fmt, __FUNCTION__, ##args)
+#define iprintf(fmt, args...) IOStream_print(iostdout, "[INFO]  %s" fmt, __FUNCTION__, ##args)
+#define eprintf(fmt, args...) IOStream_print(iostderr, "[ERROR] %s" fmt, __FUNCTION__, ##args)
+
+
+#define ARRAY_SIZE(ary) (sizeof(ary)/sizeof((ary)[0]))
+
+/* Messages are coded as msgid followed by message fields.
+ * Initial message on any channel is hello - so can check version
+ * compatibility.
+ *
+ * char* -> uint16_t:n <n bytes>
+ * ints/uints go as suitable number of bytes (e.g. uint16_t is 2 bytes).
+ * optional fields go as '1' <val> or '0' (the 0/1 is 1 byte).
+ * lists go as ('1' <elt>)* '0'
+ */
+
+int marshal_flush(IOStream *io){
+    int err  = 0;
+    err = IOStream_flush(io);
+    return err;
+}
+
+int marshal_bytes(IOStream *io, void *s, uint32_t s_n){
+    int err = 0;
+    int n;
+    n = IOStream_write(io, s, s_n);
+    if(n < 0){
+        err = n;
+    } else if (n < s_n){
+        wprintf("> Wanted %d, got %d\n", s_n, n);
+        err = -EIO;
+    }
+    return err;
+}
+
+int unmarshal_bytes(IOStream *io, void *s, uint32_t s_n){
+    int err = 0;
+    int n;
+    //dprintf("> s_n=%d\n", s_n);
+    n = IOStream_read(io, s, s_n);
+    //dprintf("> n=%d\n", n);
+    if(n < 0){
+        err = n;
+    } else if(n < s_n){
+        wprintf("> Wanted %d, got %d\n", s_n, n);
+        err = -EIO;
+    }
+    //dprintf("< err=%d\n", err);
+    return err;
+}
+
+int marshal_uint8(IOStream *io, uint8_t x){
+    return marshal_bytes(io, &x, sizeof(x));
+}
+
+int unmarshal_uint8(IOStream *io, uint8_t *x){
+    return unmarshal_bytes(io, x, sizeof(*x));
+}
+
+int marshal_uint16(IOStream *io, uint16_t x){
+    x = htons(x);
+    return marshal_bytes(io, &x, sizeof(x));
+}
+
+int unmarshal_uint16(IOStream *io, uint16_t *x){
+    int err = 0;
+    err = unmarshal_bytes(io, x, sizeof(*x));
+    *x = ntohs(*x);
+    return err;
+}
+
+int marshal_int32(IOStream *io, int32_t x){
+    int err = 0;
+    //dprintf("> x=%d\n", x);
+    x = htonl(x);
+    err = marshal_bytes(io, &x, sizeof(x));
+    //dprintf("< err=%d\n", err);
+    return err;
+}
+
+int unmarshal_int32(IOStream *io, int32_t *x){
+    int err = 0;
+    //dprintf(">\n");
+    err = unmarshal_bytes(io, x, sizeof(*x));
+    *x = ntohl(*x);
+    //dprintf("< err=%d x=%d\n", err, *x);
+    return err;
+}
+
+int marshal_uint32(IOStream *io, uint32_t x){
+    int err = 0;
+    //dprintf("> x=%u\n", x);
+    x = htonl(x);
+    err = marshal_bytes(io, &x, sizeof(x));
+    //dprintf("< err=%d\n", err);
+    return err;
+}
+
+int unmarshal_uint32(IOStream *io, uint32_t *x){
+    int err = 0;
+    //dprintf(">\n");
+    err = unmarshal_bytes(io, x, sizeof(*x));
+    *x = ntohl(*x);
+    //dprintf("< err=%d x=%u\n", err, *x);
+    return err;
+}
+
+int marshal_uint64(IOStream *io, uint64_t x){
+    int err;
+    err = marshal_uint32(io, (uint32_t) ((x >> 32) & 0xffffffff));
+    if(err) goto exit;
+    err = marshal_uint32(io, (uint32_t) ( x        & 0xffffffff));
+  exit:
+    return err;
+}
+
+int unmarshal_uint64(IOStream *io, uint64_t *x){
+    int err = 0;
+    uint32_t hi, lo;
+    err = unmarshal_uint32(io, &hi);
+    if(err) goto exit;
+    err = unmarshal_uint32(io, &lo);
+    *x = (((uint64_t) hi) << 32) | lo;
+  exit:
+    return err;
+}
+
+int marshal_net16(IOStream *io, net16_t x){
+    return marshal_bytes(io, &x, sizeof(x));
+}
+
+int unmarshal_net16(IOStream *io, net16_t *x){
+    int err = 0;
+    err = unmarshal_bytes(io, x, sizeof(*x));
+    return err;
+}
+
+int marshal_net32(IOStream *io, net32_t x){
+    return marshal_bytes(io, &x, sizeof(x));
+}
+
+int unmarshal_net32(IOStream *io, net32_t *x){
+    int err = 0;
+    err = unmarshal_bytes(io, x, sizeof(*x));
+    return err;
+}
+
+int marshal_string(IOStream *io, char *s, uint32_t s_n){
+    int err;
+    //dprintf("> s=%s\n", s);
+    err = marshal_uint32(io, s_n);
+    if(err) goto exit;
+    err = marshal_bytes(io, s, s_n);
+  exit:
+    //dprintf("< err=%d\n", err);
+    return err;
+}
+
+int unmarshal_string(IOStream *io, char *s, uint32_t s_n){
+    int err = 0, val_n = 0;
+    //dprintf(">\n");
+    err = unmarshal_uint32(io, &val_n);
+    if(err) goto exit;
+    if(val_n >= s_n){
+        err = -EINVAL;
+        goto exit;
+    }
+    err = unmarshal_bytes(io, s, val_n);
+    if(err) goto exit;
+    s[val_n] = '\0';
+  exit:
+    //dprintf("< err=%d s=%s\n", err, s);
+    return err;
+}
+
+int unmarshal_new_string(IOStream *io, char **s, uint32_t *s_n){
+    int err = 0, val_n = 0;
+    char *val = NULL;
+    //dprintf(">\n");
+    err = unmarshal_uint32(io, &val_n);
+    if(err) goto exit;
+    val = allocate(val_n + 1);
+    if(!val){
+        err = -ENOMEM;
+        goto exit;
+    }
+    err = unmarshal_bytes(io, val, val_n);
+    if(err) goto exit;
+    val[val_n] = '\0';
+  exit:
+    if(err){
+        if(val) deallocate(val);
+        val = NULL;
+        val_n = 0;
+    }
+    *s = val;
+    if(s_n) *s_n = val_n;
+    //dprintf("< err=%d s=%s\n", err, *s);
+    return err;
+}
diff --git a/tools/lib/marshal.h b/tools/lib/marshal.h
new file mode 100644 (file)
index 0000000..9a9d465
--- /dev/null
@@ -0,0 +1,43 @@
+/* $Id: marshal.h,v 1.1 2003/10/17 15:48:43 mjw Exp $ */
+#ifndef _SP_MARSHAL_H_
+#define _SP_MARSHAL_H_
+
+#include "iostream.h"
+
+/** A 16-bit uint in network order, e.g. a port number. */
+typedef uint16_t net16_t;
+
+/** A 32-bit uint in network order, e.g. an IP address. */
+typedef uint32_t net32_t;
+
+extern int marshal_flush(IOStream *io);
+
+extern int marshal_bytes(IOStream *io, void *s, uint32_t s_n);
+extern int unmarshal_bytes(IOStream *io, void *s, uint32_t s_n);
+
+extern int marshal_uint8(IOStream *io, uint8_t x);
+extern int unmarshal_uint8(IOStream *io, uint8_t *x);
+
+extern int marshal_uint16(IOStream *io, uint16_t x);
+extern int unmarshal_uint16(IOStream *io, uint16_t *x);
+
+extern int marshal_uint32(IOStream *io, uint32_t x);
+extern int unmarshal_uint32(IOStream *io, uint32_t *x);
+
+extern int marshal_int32(IOStream *io, int32_t x);
+extern int unmarshal_int32(IOStream *io, int32_t *x);
+
+extern int marshal_uint64(IOStream *io, uint64_t x);
+extern int unmarshal_uint64(IOStream *io, uint64_t *x);
+
+extern int marshal_net16(IOStream *io, net16_t x);
+extern int unmarshal_net16(IOStream *io, net16_t *x);
+
+extern int marshal_net32(IOStream *io, net32_t x);
+extern int unmarshal_net32(IOStream *io, net32_t *x);
+
+extern int marshal_string(IOStream *io, char *s, uint32_t s_n);
+extern int unmarshal_string(IOStream *io, char *s, uint32_t s_n);
+extern int unmarshal_new_string(IOStream *io, char **s, uint32_t *s_n);
+
+#endif /* ! _SP_MARSHAL_H_ */
diff --git a/tools/lib/socket_stream.c b/tools/lib/socket_stream.c
new file mode 100644 (file)
index 0000000..cfa6e3a
--- /dev/null
@@ -0,0 +1,259 @@
+/* $Id: socket_stream.c,v 1.9 2004/03/05 14:45:34 mjw Exp $ */
+/*
+ * Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/** @file
+ * An IOStream implementation using sockets.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include "allocate.h"
+#include "socket_stream.h"
+
+#define MODULE_NAME "sock"
+#define DEBUG 0
+//#undef DEBUG
+#include "debug.h"
+
+static int socket_read(IOStream *s, void *buf, size_t n);
+static int socket_write(IOStream *s, const void *buf, size_t n);
+static int socket_error(IOStream *s);
+static int socket_close(IOStream *s);
+static void socket_free(IOStream *s);
+static int socket_flush(IOStream *s);
+
+/** Methods used by a socket IOStream. */
+static const IOMethods socket_methods = {
+    read:  socket_read,
+    write: socket_write,
+    error: socket_error,
+    close: socket_close,
+    free:  socket_free,
+    flush: socket_flush,
+};
+
+/** Get the socket data.
+ * 
+ * @param io socket stream
+ * @return data
+ */
+static inline SocketData * socket_data(IOStream *io){
+    return (SocketData *)io->data;
+}
+
+/** Test if a stream is a socket stream.
+ *
+ * @param io stream
+ * @return 0 if a socket stream, -EINVAL if not
+ */
+int socket_stream_check(IOStream *io){
+    return (io && io->methods == &socket_methods ? 0 : -EINVAL);
+}
+
+/** Get the data for a socket stream.
+ *
+ * @param io stream
+ * @param data return value for the data
+ * @return 0 if a socket stream, -EINVAL if not
+ */
+int socket_stream_data(IOStream *io, SocketData **data){
+    int err = socket_stream_check(io);
+    if(err){
+        *data = NULL;
+    } else {
+        *data = socket_data(io);
+    }
+    return err;
+}
+
+/** Set the destination address for a socket stream.
+ *
+ * @param io stream
+ * @param addr address
+ * @return 0 if a socket stream, -EINVAL if not
+ */
+int socket_stream_set_addr(IOStream *io, struct sockaddr_in *addr){
+    int err = 0;
+    SocketData *data = NULL;
+    err = socket_stream_data(io, &data);
+    if(!err){
+        data->daddr = *addr;
+    }
+    return err;
+}
+
+/** Set the send flags for a socket stream.
+ *
+ * @param io stream
+ * @param flags flags
+ * @return 0 if a socket stream, -EINVAL if not
+ */
+int socket_stream_set_flags(IOStream *io, int flags){
+    int err = 0;
+    SocketData *data = NULL;
+    err = socket_stream_data(io, &data);
+    if(!err){
+        data->flags = flags;
+    }
+    return err;
+}
+
+/** Write to the underlying socket using sendto.
+ *
+ * @param stream input
+ * @param buf where to put input
+ * @param n number of bytes to write
+ * @return number of bytes written
+ */
+static int socket_write(IOStream *s, const void *buf, size_t n){
+    SocketData *data = socket_data(s);
+    struct sockaddr *daddr = (struct sockaddr *)&data->daddr;
+    socklen_t daddr_n = sizeof(data->daddr);
+    int k;
+    dprintf("> sock=%d addr=%s:%d n=%d\n",
+            data->fd, inet_ntoa(data->daddr.sin_addr), ntohs(data->daddr.sin_port), n);
+    if(0){
+        struct sockaddr_in self = {};
+        socklen_t self_n;
+        getsockname(data->fd, (struct sockaddr *)&self, &self_n);
+        dprintf("> sockname sock=%d %s:%d\n",
+                data->fd, inet_ntoa(self.sin_addr), ntohs(self.sin_port));
+    }
+    k = sendto(data->fd, buf, n, data->flags, daddr, daddr_n);
+    dprintf("> sendto=%d\n", k);
+    return k;
+}
+
+/** Read from the underlying stream using recv();
+ *
+ * @param stream input
+ * @param buf where to put input
+ * @param n number of bytes to read
+ * @return number of bytes read
+ */
+static int socket_read(IOStream *s, void *buf, size_t n){
+    SocketData *data = socket_data(s);
+    int k;
+    struct sockaddr *saddr = (struct sockaddr *)&data->saddr;
+    socklen_t saddr_n = sizeof(data->saddr);
+    k = recvfrom(data->fd, buf, n, data->flags, saddr, &saddr_n);
+    return k;
+}
+
+/** Print to the underlying socket.
+ *
+ * @param s socket stream
+ * @param msg format to use
+ * @param args arguments
+ * @return result of the print
+ */
+static int socket_print(IOStream *s, const char *msg, va_list args){
+    SocketData *data = socket_data(s);
+    int n;
+    n = vsnprintf(data->buf, data->buf_n - 1, msg, args);
+    if(0 < n && n < data->buf_n){
+        socket_write(s, data->buf, n);
+    }
+    return n;
+}
+
+/** Read a character from the underlying socket
+ *
+ * @param s socket stream
+ * @return character read, IOSTREAM_EOF on end of socket (or error)
+ */
+static int socket_getc(IOStream *s){
+    char b;
+    int n, c;
+    n = socket_read(s, &b, 1);
+    c = (n <= 0 ? IOSTREAM_EOF : b);
+    return c;
+}
+
+/** Flush the socket (no-op).
+ *
+ * @param s socket stream
+ * @return 0 on success, error code otherwise
+ */
+static int socket_flush(IOStream *s){
+    return 0;
+}
+
+/** Check if a socket stream has an error (no-op).
+ *
+ * @param s socket stream
+ * @return 1 if has an error, 0 otherwise
+ */
+static int socket_error(IOStream *s){
+    // Read SOL_SOCKET/SO_ERROR ?
+    return 0;
+}
+
+/** Close a socket stream.
+ *
+ * @param s socket stream to close
+ * @return result of the close
+ */
+static int socket_close(IOStream *s){
+    SocketData *data = socket_data(s);
+    return close(data->fd);
+}
+
+/** Free a socket stream.
+ *
+ * @param s socket stream
+ */
+static void socket_free(IOStream *s){
+    SocketData *data = socket_data(s);
+    deallocate(data);
+}
+
+/** Create an IOStream for a socket.
+ *
+ * @param fd socket to wtap
+ * @return new IOStream using fd for i/o
+ */
+IOStream *socket_stream_new(int fd){
+    int err = -ENOMEM;
+    IOStream *io = NULL;
+    SocketData *data = NULL;
+
+    io = ALLOCATE(IOStream);
+    if(!io) goto exit;
+    io->methods = &socket_methods;
+    data = ALLOCATE(SocketData);
+    if(!data) goto exit;
+    io->data = data;
+    data->fd = fd;
+    data->buf_n = sizeof(data->buf);
+    err = 0;
+  exit:
+    if(err){
+        if(io){
+            if(data) deallocate(data);
+            deallocate(io);
+            io = NULL;
+        }
+    }
+    return io;
+}
+
diff --git a/tools/lib/socket_stream.h b/tools/lib/socket_stream.h
new file mode 100644 (file)
index 0000000..5b8515f
--- /dev/null
@@ -0,0 +1,54 @@
+/* $Id: socket_stream.h,v 1.2 2004/03/04 17:38:13 mjw Exp $ */
+/*
+ * Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef _XEN_LIB_SOCKET_STREAM_H_
+#define _XEN_LIB_SOCKET_STREAM_H_
+
+#ifndef __KERNEL__
+#include "iostream.h"
+#include <stdio.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+/** Data associated with a socket stream. */
+typedef struct SocketData {
+    /** The socket file descriptor. */
+    int fd;
+    /** Source address from last read (recvfrom). */
+    struct sockaddr_in saddr;
+    /** Destination address for writes (sendto). */
+    struct sockaddr_in daddr;
+    /** Write flags (sendto). */
+    int flags;
+    /** Buffer size. */
+    int buf_n;
+    /** Buffer for formatted printing. */
+    char buf[1024];
+} SocketData;
+
+extern IOStream *socket_stream_new(int fd);
+extern int socket_stream_data(IOStream *io, SocketData **data);
+extern int socket_stream_check(IOStream *io);
+extern int socket_stream_set_addr(IOStream *io, struct sockaddr_in *addr);
+extern int socket_stream_set_flags(IOStream *io, int flags);
+
+#endif
+#endif /* !_XEN_LIB_SOCKET_STREAM_H_ */
diff --git a/tools/lib/string_stream.c b/tools/lib/string_stream.c
new file mode 100644 (file)
index 0000000..c3cf423
--- /dev/null
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2001, 2002 Hewlett-Packard Company.
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/** @file
+ * IOStream subtype for input and output to strings.
+ * Usable from user or kernel code (with __KERNEL__ defined).
+ */
+
+#include "sys_string.h"
+#include "string_stream.h"
+#include "allocate.h"
+
+static int string_print(IOStream *io, const char *msg, va_list args);
+static int string_getc(IOStream *io);
+static int string_error(IOStream *io);
+static int string_close(IOStream *io);
+static void string_free(IOStream *io);
+
+/** Methods for a string stream. */
+static IOMethods string_methods = {
+    //print: string_print,
+    //getc:  string_getc,
+    error: string_error,
+    close: string_close,
+    free:  string_free,
+};
+
+/** Get the string stream state.
+ *
+ * @param io string stream
+ * @return state
+ */
+static inline StringData *get_string_data(IOStream *io){
+    return (StringData*)io->data;
+}
+
+/** Read a character from a string stream.
+ *
+ * @param io string stream
+ * @return character read, IOSTREAM_EOF if no more input
+ */
+static int string_getc(IOStream *io){
+    StringData *data = get_string_data(io);
+    int c = IOSTREAM_EOF;
+    char *s = data->in;
+
+    if(s && s < data->end){
+        c = (unsigned)*s;
+        data->in = s+1;
+    }
+    return c;
+}
+
+/** Print to a string stream.
+ * Formats the data to an internal buffer and prints it.
+ * The formatted data must fit into the internal buffer.
+ *
+ * @param io string stream
+ * @param format print format
+ * @param args print arguments
+ * @return result of the print
+ */
+static int string_print(IOStream *io, const char *msg, va_list args){
+    StringData *data = get_string_data(io);
+    int k = data->end - data->out;
+    int n = vsnprintf(data->out, k, (char*)msg, args);
+    if(n < 0 || n > k ){
+        n = k;
+        IOStream_close(io);
+    } else {
+        data->out += n;
+    }
+    return n;
+}
+
+/** Test if a string stream has an error.
+ *
+ * @param io string stream
+ * @return 0 if ok, error code otherwise
+ */
+static int string_error(IOStream *io){
+    StringData *data = get_string_data(io);
+    return data->out == NULL;
+}
+
+/** Close a string stream.
+ *
+ * @param io string stream
+ * @return 0
+ */
+static int string_close(IOStream *io){
+    StringData *data = get_string_data(io);
+    data->in = NULL;
+    data->out = NULL;
+    return 0;
+}
+
+/** Free a string stream.
+ * The stream must have been allocated, not statically created.
+ * The stream state is freed, but the underlying string is not.
+ *
+ * @param io string stream
+ */
+static void string_free(IOStream *io){
+    StringData *data = get_string_data(io);
+    zero(data, sizeof(*data));
+    deallocate(data);
+}
+
+/** Get the methods to use for a string stream.
+ *
+ * @return methods
+ */
+IOMethods *string_stream_get_methods(void){
+    return &string_methods;
+}
+
+/** Initialise a string stream, usually from static data.
+ *
+ * @param io address of IOStream to fill in
+ * @param data address of StringData to fill in
+ * @param s string to use
+ * @param n length of the string
+ */
+void string_stream_init(IOStream *io, StringData *data, char *s, int n){
+    if(data && io){
+        zero(data, sizeof(*data));
+        data->string = (char*)s;
+        data->in = data->string;
+        data->out = data->string;
+        data->size = n;
+        data->end = data->string + n;
+        zero(io, sizeof(*io));
+        io->methods = &string_methods;
+        io->data = data;
+    }
+}
+
+/** Allocate and initialise a string stream.
+ *
+ * @param s string to use
+ * @param n length of the string
+ * @return new stream (free using IOStream_free)
+ */
+IOStream *string_stream_new(char *s, int n){
+    int ok = 0;
+    StringData *data = ALLOCATE(StringData);
+    IOStream *io = ALLOCATE(IOStream);
+    if(data && io){
+        ok = 1;
+        string_stream_init(io, data, s, n);
+    }
+    if(!ok){
+        deallocate(data);
+        deallocate(io);
+        io = NULL;
+    }
+    return io;
+}
diff --git a/tools/lib/string_stream.h b/tools/lib/string_stream.h
new file mode 100644 (file)
index 0000000..36d764b
--- /dev/null
@@ -0,0 +1,46 @@
+/* $Id: string_stream.h,v 1.1 2003/08/22 14:25:48 mjw Exp $ */
+/*
+ * Copyright (C) 2001, 2002 Hewlett-Packard Company.
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef _SP_STRING_STREAM_H_
+#define _SP_STRING_STREAM_H_
+
+#include "iostream.h"
+
+/** Internal state for a string stream.
+ * Exposed here so that string streams can be statically created, using
+ * string_stream_init().
+ */
+typedef struct {
+    /** The string used for input and ouput. */
+    char *string;
+    /** Output pointer. */
+    char *out;
+    /** Input pointer. */
+    char *in;
+    /** Length of string. */
+    int size;
+    /** End marker. */
+    char *end;
+} StringData;
+
+extern IOMethods *string_stream_get_methods(void);
+extern IOStream *string_stream_new(char *s, int n);
+extern void string_stream_init(IOStream *stream, StringData *data, char *s, int n);
+
+#endif /* !_SP_STRING_STREAM_H_ */
diff --git a/tools/lib/sxpr.c b/tools/lib/sxpr.c
new file mode 100644 (file)
index 0000000..adeffbe
--- /dev/null
@@ -0,0 +1,935 @@
+/*
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of the
+ * License, or  (at your option) any later version. This library is 
+ * distributed in the  hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include <stdarg.h>
+#include "sys_string.h"
+#include "lexis.h"
+#include "sys_net.h"
+#include "hash_table.h"
+#include "sxpr.h"
+
+#include <errno.h>
+#undef free
+
+/** @file
+ * General representation of sxprs.
+ * Includes print, equal, and free functions for the sxpr types.
+ *
+ * Zero memory containing an Sxpr will have the value ONONE - this is intentional.
+ * When a function returning an sxpr cannot allocate memory we return ONOMEM.
+ *
+ */
+
+static int atom_print(IOStream *io, Sxpr obj, unsigned flags);
+static int atom_equal(Sxpr x, Sxpr y);
+static void atom_free(Sxpr obj);
+
+static int string_print(IOStream *io, Sxpr obj, unsigned flags);
+static int string_equal(Sxpr x, Sxpr y);
+static void string_free(Sxpr obj);
+
+static int cons_print(IOStream *io, Sxpr obj, unsigned flags);
+static int cons_equal(Sxpr x, Sxpr y);
+static void cons_free(Sxpr obj);
+
+static int null_print(IOStream *io, Sxpr obj, unsigned flags);
+static int none_print(IOStream *io, Sxpr obj, unsigned flags);
+static int int_print(IOStream *io, Sxpr obj, unsigned flags);
+static int bool_print(IOStream *io, Sxpr obj, unsigned flags);
+
+/** Type definitions. */
+static SxprType types[1024] = {
+    [T_NONE]     { type:    T_NONE,     name: "none",       print: none_print      },
+    [T_NULL]     { type:    T_NULL,     name: "null",       print: null_print      },
+    [T_UINT]     { type:    T_UINT,     name: "int",        print: int_print,      },
+    [T_BOOL]     { type:    T_BOOL,     name: "bool",       print: bool_print,     },
+    [T_ATOM]     { type:    T_ATOM,     name: "atom",       print: atom_print,
+                  pointer: TRUE,
+                  free:    atom_free,
+                  equal:   atom_equal,
+                },
+    [T_STRING]   { type:    T_STRING,   name: "string",     print: string_print,
+                  pointer: TRUE,
+                  free:    string_free,
+                  equal:   string_equal,
+                },
+    [T_CONS]     { type:    T_CONS,     name: "cons",       print: cons_print,
+                  pointer: TRUE,
+                  free:    cons_free,
+                  equal:   cons_equal,
+                },
+};
+
+/** Number of entries in the types array. */
+static int type_sup = sizeof(types)/sizeof(types[0]);
+
+/** Get the type definition for a given type code.
+ *
+ * @param ty type code
+ * @return type definition or null
+ */
+SxprType *get_sxpr_type(int ty){
+    if(0 <= ty && ty < type_sup){
+        return types+ty;
+    }
+    return NULL;
+}
+
+/** The default print function.
+ *
+ * @param io stream to print to
+ * @param x sxpr to print
+ * @param flags print flags
+ * @return number of bytes written on success
+ */
+int default_print(IOStream *io, Sxpr x, unsigned flags){
+    return IOStream_print(io, "#<%u %lu>\n", get_type(x), get_ul(x));
+}
+
+/** The default equal function.
+ * Uses eq().
+ *
+ * @param x sxpr to compare
+ * @param y sxpr to compare
+ * @return 1 if equal, 0 otherwise
+ */
+int default_equal(Sxpr x, Sxpr y){
+    return eq(x, y);
+}
+
+/** General sxpr print function.
+ * Prints an sxpr on a stream using the print function for the sxpr type.
+ * Printing is controlled by flags from the PrintFlags enum.
+ * If PRINT_TYPE is in the flags the sxpr type is printed before the sxpr
+ * (for debugging).
+ *
+ * @param io stream to print to
+ * @param x sxpr to print
+ * @param flags print flags
+ * @return number of bytes written
+ */
+int objprint(IOStream *io, Sxpr x, unsigned flags){
+    SxprType *def = get_sxpr_type(get_type(x));
+    ObjPrintFn *print_fn = (def && def->print ? def->print : default_print);
+    int k = 0;
+    if(!io) return k;
+    if(flags & PRINT_TYPE){
+       k += IOStream_print(io, "%s:", def->name);
+    }
+    k += print_fn(io, x, flags);
+    return k;
+}
+
+/** General sxpr free function.
+ * Frees an sxpr using the free function for its type.
+ * Free functions must recursively free any subsxprs.
+ * If no function is defined then the default is to
+ * free sxprs whose type has pointer true.
+ * Sxprs must not be used after freeing.
+ *
+ * @param x sxpr to free
+ */
+void objfree(Sxpr x){
+    SxprType *def = get_sxpr_type(get_type(x));
+
+    if(def){
+       if(def->free){
+           def->free(x);
+       } else if (def->pointer){
+           hfree(x);
+       }
+    }
+}
+
+/** General sxpr equality function.
+ * Compares x and y using the equal function for x.
+ * Uses default_equal() if x has no equal function.
+ *
+ * @param x sxpr to compare
+ * @param y sxpr to compare
+ * @return 1 if equal, 0 otherwise
+ */
+int objequal(Sxpr x, Sxpr y){
+    SxprType *def = get_sxpr_type(get_type(x));
+    ObjEqualFn *equal_fn = (def && def->equal ? def->equal : default_equal);
+    return equal_fn(x, y);
+}
+
+/** Search for a key in an alist.
+ * An alist is a list of conses, where the cars
+ * of the conses are the keys. Compares keys using equality.
+ *
+ * @param k key
+ * @param l alist to search
+ * @return first element of l with car k, or ONULL
+ */
+Sxpr assoc(Sxpr k, Sxpr l){
+    for( ; CONSP(l) ; l = CDR(l)){
+        Sxpr x = CAR(l);
+        if(CONSP(x) && objequal(k, CAR(x))){
+            return x;   
+        }
+    }
+    return ONULL;
+}
+
+/** Search for a key in an alist.
+ * An alist is a list of conses, where the cars
+ * of the conses are the keys. Compares keys using eq.
+ *
+ * @param k key
+ * @param l alist to search
+ * @return first element of l with car k, or ONULL
+ */
+Sxpr assocq(Sxpr k, Sxpr l){
+    for( ; CONSP(l); l = CDR(l)){
+        Sxpr x = CAR(l);
+        if(CONSP(x) && eq(k, CAR(x))){
+            return x;
+        }
+    }
+    return ONULL;
+}
+
+/** Add a new key and value to an alist.
+ *
+ * @param k key
+ * @param l value
+ * @param l alist
+ * @return l with the new cell added to the front
+ */
+Sxpr acons(Sxpr k, Sxpr v, Sxpr l){
+    Sxpr x, y;
+    x = cons_new(k, v);
+    if(NOMEMP(x)) return x;
+    y = cons_new(x, l);
+    if(NOMEMP(y)) cons_free_cells(x);
+    return y;
+}
+
+/** Test if a list contains an element.
+ * Uses sxpr equality.
+ *
+ * @param l list
+ * @param x element to look for
+ * @return a tail of l with x as car, or ONULL
+ */
+Sxpr cons_member(Sxpr l, Sxpr x){
+    for( ; CONSP(l) && !eq(x, CAR(l)); l = CDR(l)){}
+    return l;
+}
+
+/** Test if a list contains an element satisfying a test.
+ * The test function is called with v and an element of the list.
+ *
+ * @param l list
+ * @param test_fn test function to use
+ * @param v value for first argument to the test
+ * @return a tail of l with car satisfying the test, or 0
+ */
+Sxpr cons_member_if(Sxpr l, ObjEqualFn *test_fn, Sxpr v){
+    for( ; CONSP(l) && !test_fn(v, CAR(l)); l = CDR(l)){ }
+    return l;
+}
+
+/** Test if the elements of list 't' are a subset of the elements
+ * of list 's'. Element order is not significant.
+ *
+ * @param s element list to check subset of
+ * @param t element list to check if is a subset
+ * @return 1 if is a subset, 0 otherwise
+ */
+int cons_subset(Sxpr s, Sxpr t){
+    for( ; CONSP(t); t = CDR(t)){
+       if(!CONSP(cons_member(s, CAR(t)))){
+           return 0;
+       }
+    }
+    return 1;
+}
+
+/** Test if two lists have equal sets of elements.
+ * Element order is not significant.
+ *
+ * @param s list to check
+ * @param t list to check
+ * @return 1 if equal, 0 otherwise
+ */
+int cons_set_equal(Sxpr s, Sxpr t){
+    return cons_subset(s, t) && cons_subset(t, s);
+}
+
+#ifdef USE_GC
+/*============================================================================*/
+/* The functions inside this ifdef are only safe if GC is used.
+ * Otherwise they may leak memory.
+ */
+
+/** Remove an element from a list (GC only).
+ * Uses sxpr equality and removes all instances, even
+ * if there are more than one.
+ *
+ * @param l list to remove elements from
+ * @param x element to remove
+ * @return modified input list
+ */
+Sxpr cons_remove(Sxpr l, Sxpr x){
+    return cons_remove_if(l, eq, x);
+}
+
+/** Remove elements satisfying a test (GC only).
+ * The test function is called with v and an element of the set.
+ *
+ * @param l list to remove elements from
+ * @param test_fn function to use to decide if an element should be removed
+ * @return modified input list
+ */
+Sxpr cons_remove_if(Sxpr l, ObjEqualFn *test_fn, Sxpr v){
+    Sxpr prev = ONULL, elt, next;
+
+    for(elt = l; CONSP(elt); elt = next){
+        next = CDR(elt);
+        if(test_fn(v, CAR(elt))){
+            if(NULLP(prev)){
+                l = next;
+            } else {
+                CDR(prev) = next;
+            }
+        }
+    }
+    return l;
+}
+
+/** Set the value for a key in an alist (GC only).
+ * If the key is present, changes the value, otherwise
+ * adds a new cell.
+ *
+ * @param k key
+ * @param v value
+ * @param l alist
+ * @return modified or extended list
+ */
+Sxpr setf(Sxpr k, Sxpr v, Sxpr l){
+    Sxpr e = assoc(k, l);
+    if(NULLP(e)){
+        l = acons(k, v, l);
+    } else {
+        CAR(CDR(e)) = v;
+    }
+    return l;
+}
+/*============================================================================*/
+#endif /* USE_GC */
+
+/** Create a new atom with the given name.
+ *
+ * @param name the name
+ * @return new atom
+ */
+Sxpr atom_new(char *name){
+    Sxpr n, obj = ONOMEM;
+
+    n = string_new(name);
+    if(NOMEMP(n)) goto exit;
+    obj = HALLOC(ObjAtom, T_ATOM);
+    if(NOMEMP(obj)) goto exit;
+    OBJ_ATOM(obj)->name = n;
+  exit:
+    return obj;
+}
+
+/** Free an atom.
+ *
+ * @param obj to free
+ */
+void atom_free(Sxpr obj){
+    // Interned atoms are shared, so do not free.
+    if(OBJ_ATOM(obj)->interned) return;
+    objfree(OBJ_ATOM(obj)->name);
+    hfree(obj);
+}
+
+/** Print an atom. Prints the atom name.
+ *
+ * @param io stream to print to
+ * @param obj to print
+ * @param flags print flags
+ * @return number of bytes printed
+ */
+int atom_print(IOStream *io, Sxpr obj, unsigned flags){
+    //return string_print(io, OBJ_ATOM(obj)->name, (flags | PRINT_RAW));
+    return string_print(io, OBJ_ATOM(obj)->name, flags);
+}
+
+/** Atom equality.
+ *
+ * @param x to compare
+ * @param y to compare
+ * @return 1 if equal, 0 otherwise
+ */
+int atom_equal(Sxpr x, Sxpr y){
+    int ok;
+    ok = eq(x, y);
+    if(ok) goto exit;
+    ok = ATOMP(y) && string_equal(OBJ_ATOM(x)->name, OBJ_ATOM(y)->name);
+    if(ok) goto exit;
+    ok = STRINGP(y) && string_equal(OBJ_ATOM(x)->name, y);
+  exit:
+    return ok;
+}
+
+/** Get the name of an atom.
+ *
+ * @param obj atom
+ * @return name
+ */
+char * atom_name(Sxpr obj){
+    return string_string(OBJ_ATOM(obj)->name);
+}
+
+/** Get the C string from a string sxpr.
+ *
+ * @param obj string sxpr
+ * @return string
+ */
+char * string_string(Sxpr obj){
+    return OBJ_STRING(obj);
+}
+
+/** Get the length of a string.
+ *
+ * @param obj string
+ * @return length
+ */
+int string_length(Sxpr obj){
+    return strlen(OBJ_STRING(obj));
+}
+
+/** Create a new string. The input string is copied,
+ * and must be null-terminated.
+ *
+ * @param s characters to put in the string
+ * @return new sxpr
+ */
+Sxpr string_new(char *s){
+    int n = (s ? strlen(s) : 0);
+    Sxpr obj;
+    obj = halloc(n+1, T_STRING);
+    if(!NOMEMP(obj)){
+        char *str = OBJ_STRING(obj);
+        strncpy(str, s, n);
+        str[n] = '\0';
+    }
+    return obj;
+}
+
+/** Free a string.
+ *
+ * @param obj to free
+ */
+void string_free(Sxpr obj){
+    hfree(obj);
+}
+
+/** Determine if a string needs escapes when printed
+ * using the given flags.
+ *
+ * @param str string to check
+ * @param flags print flags
+ * @return 1 if needs escapes, 0 otherwise
+ */
+int needs_escapes(char *str, unsigned flags){
+    char *c;
+    int val = 0;
+
+    if(str){
+       for(c=str; *c; c++){
+           if(in_alpha_class(*c)) continue;
+           if(in_decimal_digit_class(*c)) continue;
+           if(in_class(*c, "/._+:@~-")) continue;
+           val = 1;
+           break;
+       }
+    }
+    //printf("\n> val=%d str=|%s|\n", val, str);
+    return val;
+}
+
+/** Print a string to a stream, with escapes if necessary.
+ *
+ * @param io stream to print to
+ * @param str string
+ * @param flags print flags
+ * @return number of bytes written
+ */
+int _string_print(IOStream *io, char *str, unsigned flags){
+    int k = 0;
+    if((flags & PRINT_RAW) || !needs_escapes(str, flags)){
+        k += IOStream_print(io, str);
+    } else {
+       k += IOStream_print(io, "\"");
+       if(str){
+            char *s;
+            for(s = str; *s; s++){
+                if(*s < ' ' || *s >= 127 ){
+                    switch(*s){
+                    case '\a': k += IOStream_print(io, "\\a");  break;
+                    case '\b': k += IOStream_print(io, "\\b");  break;
+                    case '\f': k += IOStream_print(io, "\\f");  break;
+                    case '\n': k += IOStream_print(io, "\\n");  break;
+                    case '\r': k += IOStream_print(io, "\\r");  break;
+                    case '\t': k += IOStream_print(io, "\\t");  break;
+                    case '\v': k += IOStream_print(io, "\\v");  break;
+                    default:
+                        // Octal escape;
+                        k += IOStream_print(io, "\\%o", *s);
+                        break;
+                    }
+                } else if(*s == c_double_quote ||
+                          *s == c_single_quote ||
+                          *s == c_escape){
+                    k += IOStream_print(io, "\\%c", *s);
+                } else {
+                    k+= IOStream_print(io, "%c", *s);
+                }
+            }
+       }
+       k += IOStream_print(io, "\"");
+    }
+    return k;
+}
+
+/** Print a string to a stream, with escapes if necessary.
+ *
+ * @param io stream to print to
+ * @param obj string
+ * @param flags print flags
+ * @return number of bytes written
+ */
+int string_print(IOStream *io, Sxpr obj, unsigned flags){
+    return _string_print(io, OBJ_STRING(obj), flags);
+}
+
+/** Compare an sxpr with a string for equality.
+ *
+ * @param x string to compare with
+ * @param y sxpr to compare
+ * @return 1 if equal, 0 otherwise
+ */
+int string_equal(Sxpr x, Sxpr y){
+    int ok = 0;
+    ok = eq(x,y);
+    if(ok) goto exit;
+    ok = has_type(y, T_STRING) && !strcmp(OBJ_STRING(x), OBJ_STRING(y));
+    if(ok) goto exit;
+    ok = has_type(y, T_ATOM) && !strcmp(OBJ_STRING(x), atom_name(y));
+  exit:
+    return ok;
+}
+
+/** Create a new cons cell.
+ * The cell is ONOMEM if either argument is.
+ *
+ * @param car sxpr for the car
+ * @param cdr sxpr for the cdr
+ * @return new cons
+ */
+Sxpr cons_new(Sxpr car, Sxpr cdr){
+    Sxpr obj;
+    if(NOMEMP(car) || NOMEMP(cdr)){
+        obj = ONOMEM;
+    } else {
+        obj = HALLOC(ObjCons, T_CONS);
+        if(!NOMEMP(obj)){
+            ObjCons *z = OBJ_CONS(obj);
+            z->car = car;
+            z->cdr = cdr;
+        }
+    }
+    return obj;
+}
+
+/** Push a new element onto a list.
+ *
+ * @param list list to add to
+ * @param elt element to add
+ * @return 0 if successful, error code otherwise
+ */
+int cons_push(Sxpr *list, Sxpr elt){
+    Sxpr l;
+    l = cons_new(elt, *list);
+    if(NOMEMP(l)) return -ENOMEM;
+    *list = l;
+    return 0;
+}
+
+/** Free a cons. Recursively frees the car and cdr.
+ *
+ * @param obj to free
+ */
+void cons_free(Sxpr obj){
+    Sxpr next;
+    for(; CONSP(obj); obj = next){
+       next = CDR(obj);
+       objfree(CAR(obj));
+       hfree(obj);
+    }
+    if(!NULLP(obj)){
+       objfree(obj);
+    }
+}
+
+/** Free a cons and its cdr cells, but not the car sxprs.
+ * Does nothing if called on something that is not a cons.
+ *
+ * @param obj to free
+ */
+void cons_free_cells(Sxpr obj){
+    Sxpr next;
+    for(; CONSP(obj); obj = next){
+       next = CDR(obj);
+       hfree(obj);
+    }
+}
+
+/** Print a cons.
+ * Prints the cons in list format if the cdrs are conses.
+ * uses pair (dot) format if the last cdr is not a cons (or null).
+ *
+ * @param io stream to print to
+ * @param obj to print
+ * @param flags print flags
+ * @return number of bytes written
+ */
+int cons_print(IOStream *io, Sxpr obj, unsigned flags){
+    int first = 1;
+    int k = 0;
+    k += IOStream_print(io, "(");
+    for( ; CONSP(obj) ; obj = CDR(obj)){
+        if(first){ 
+            first = 0;
+        } else {
+            k += IOStream_print(io, " ");
+        }
+        k += objprint(io, CAR(obj), flags);
+    }
+    if(!NULLP(obj)){
+        k += IOStream_print(io, " . ");
+        k += objprint(io, obj, flags);
+    }
+    k += IOStream_print(io, ")");
+    return (IOStream_error(io) ? -1 : k);
+}
+
+/** Compare a cons with another sxpr for equality.
+ * If y is a cons, compares the cars and cdrs recursively.
+ *
+ * @param x cons to compare
+ * @param y sxpr to compare
+ * @return 1 if equal, 0 otherwise
+ */
+int cons_equal(Sxpr x, Sxpr y){
+    return CONSP(y) &&
+        objequal(CAR(x), CAR(y)) &&
+        objequal(CDR(x), CDR(y));
+}
+
+/** Return the length of a cons list.
+ *
+ * @param obj list
+ * @return length
+ */
+int cons_length(Sxpr obj){
+    int count = 0;
+    for( ; CONSP(obj); obj = CDR(obj)){
+        count++;
+    }
+    return count;
+}
+
+/** Destructively reverse a cons list in-place.
+ * If the argument is not a cons it is returned unchanged.
+ * 
+ * @param l to reverse
+ * @return reversed list
+ */
+Sxpr nrev(Sxpr l){
+    if(CONSP(l)){
+       // Iterate down the cells in the list making the cdr of
+       // each cell point to the previous cell. The last cell 
+       // is the head of the reversed list.
+       Sxpr prev = ONULL;
+       Sxpr cell = l;
+       Sxpr next;
+
+       while(1){
+           next = CDR(cell);
+           CDR(cell) = prev;
+           if(!CONSP(next)) break;
+           prev = cell;
+           cell = next;
+       }
+       l = cell;
+    }
+    return l;
+}
+
+/** Print the null sxpr.       
+ *
+ * @param io stream to print to
+ * @param obj to print
+ * @param flags print flags
+ * @return number of bytes written
+ */
+static int null_print(IOStream *io, Sxpr obj, unsigned flags){
+    return IOStream_print(io, "()");
+}
+
+/** Print the `unspecified' sxpr none.
+ *
+ * @param io stream to print to
+ * @param obj to print
+ * @param flags print flags
+ * @return number of bytes written
+ */
+static int none_print(IOStream *io, Sxpr obj, unsigned flags){
+    return IOStream_print(io, "<none>");
+}
+
+/** Print an integer.
+ *
+ * @param io stream to print to
+ * @param obj to print
+ * @param flags print flags
+ * @return number of bytes written
+ */
+static int int_print(IOStream *io, Sxpr obj, unsigned flags){
+    return IOStream_print(io, "%d", OBJ_INT(obj));
+}
+
+/** Print a boolean.
+ *
+ * @param io stream to print to
+ * @param obj to print
+ * @param flags print flags
+ * @return number of bytes written
+ */
+static int bool_print(IOStream *io, Sxpr obj, unsigned flags){
+    return IOStream_print(io, (OBJ_UINT(obj) ? k_true : k_false));
+}
+
+int sxprp(Sxpr obj, Sxpr name){
+    return CONSP(obj) && objequal(CAR(obj), name);
+}
+
+/** Get the name of an element.
+ * 
+ * @param obj element
+ * @return name
+ */
+Sxpr sxpr_name(Sxpr obj){
+    Sxpr val = ONONE;
+    if(CONSP(obj)){
+        val = CAR(obj);
+    } else if(STRINGP(obj) || ATOMP(obj)){
+        val = obj;
+    }
+    return val;
+}
+
+int sxpr_is(Sxpr obj, char *s){
+    if(ATOMP(obj)) return !strcmp(atom_name(obj), s);
+    if(STRINGP(obj)) return !strcmp(string_string(obj), s);
+    return 0;
+}
+
+int sxpr_elementp(Sxpr obj, Sxpr name){
+    return CONSP(obj) && objequal(CAR(obj), name);
+}
+
+/** Get the attributes of an sxpr.
+ * 
+ * @param obj sxpr
+ * @return attributes
+ */
+Sxpr sxpr_attributes(Sxpr obj){
+    Sxpr val = ONULL;
+    if(CONSP(obj)){
+        obj = CDR(obj);
+        if(CONSP(obj)){
+            obj = CAR(obj);
+            if(sxprp(obj, intern("@"))){
+                val = CDR(obj);
+            }
+        }
+    }
+    return val;
+}
+
+Sxpr sxpr_attribute(Sxpr obj, Sxpr key, Sxpr def){
+    Sxpr val = ONONE;
+    val = assoc(sxpr_attributes(obj), key);
+    if(CONSP(val) && CONSP(CDR(val))){
+        val = CADR(def);
+    } else {
+        val = def;
+    }
+    return val;
+}
+
+/** Get the children of an sxpr.
+ * 
+ * @param obj sxpr
+ * @return children
+ */
+Sxpr sxpr_children(Sxpr obj){
+    Sxpr val = ONULL;
+    if(CONSP(obj)){
+        val = CDR(obj);
+        if(CONSP(val) && sxprp(CAR(val), intern("@"))){
+            val = CDR(val);
+        }
+    }
+    return val;
+}
+
+Sxpr sxpr_child(Sxpr obj, Sxpr name, Sxpr def){
+    Sxpr val = ONONE;
+    Sxpr l;
+    for(l = sxpr_children(obj); CONSP(l); l = CDR(l)){
+        if(sxprp(CAR(l), name)){
+            val = CAR(l);
+            break;
+        }
+    }
+    if(NONEP(val)) val = def;
+    return val;
+}
+
+Sxpr sxpr_child0(Sxpr obj, Sxpr def){
+    Sxpr val = ONONE;
+    Sxpr l = sxpr_children(obj);
+    if(CONSP(l)){
+        val = CAR(l);
+    } else {
+        val = def;
+    }
+    return val;
+}
+
+Sxpr sxpr_child_value(Sxpr obj, Sxpr name, Sxpr def){
+    Sxpr val = ONONE;
+    val = sxpr_child(obj, name, ONONE);
+    if(NONEP(val)){
+        val = def;
+    } else {
+        val = sxpr_child0(val, def);
+    }
+    return val;
+}
+
+/** Table of interned symbols. Indexed by symbol name. */
+static HashTable *symbols = NULL;
+
+/** Hash function for entries in the symbol table.
+ *
+ * @param key to hash
+ * @return hashcode
+ */
+static Hashcode sym_hash_fn(void *key){
+    return hash_string((char*)key);
+}
+
+/** Key equality function for the symbol table.
+ *
+ * @param x to compare
+ * @param y to compare
+ * @return 1 if equal, 0 otherwise
+ */
+static int sym_equal_fn(void *x, void *y){
+    return !strcmp((char*)x, (char*)y);
+}
+
+/** Entry free function for the symbol table.
+ *
+ * @param table the entry is in
+ * @param entry being freed
+ */
+static void sym_free_fn(HashTable *table, HTEntry *entry){
+    if(entry){
+       objfree(((ObjAtom*)entry->value)->name);
+       HTEntry_free(entry);
+    }
+}
+       
+/** Initialize the symbol table.
+ *
+ * @return 0 on sucess, error code otherwise
+ */
+static int init_symbols(void){
+    symbols = HashTable_new(100);
+    if(symbols){
+        symbols->key_hash_fn = sym_hash_fn;
+        symbols->key_equal_fn = sym_equal_fn;
+       symbols->entry_free_fn = sym_free_fn;
+        return 0;
+    }
+    return -1;
+}
+
+/** Cleanup the symbol table. Frees the table and all its symbols.
+ */
+void cleanup_symbols(void){
+    HashTable_free(symbols);
+    symbols = NULL;
+}
+
+/** Get the interned symbol with the given name.
+ * No new symbol is created.
+ *
+ * @return symbol or null
+ */
+Sxpr get_symbol(char *sym){
+    HTEntry *entry;
+    if(!symbols){
+       if(init_symbols()) return ONOMEM;
+       return ONULL;
+    }
+    entry = HashTable_get_entry(symbols, sym);
+    if(entry){
+        return OBJP(T_ATOM, entry->value);
+    } else {
+        return ONULL;
+    }
+}
+
+/** Get the interned symbol with the given name.
+ * Creates a new symbol if necessary.
+ *
+ * @return symbol
+ */
+Sxpr intern(char *sym){
+    Sxpr symbol = get_symbol(sym);
+    if(NULLP(symbol)){
+       if(!symbols) return ONOMEM;
+        symbol = atom_new(sym);
+        if(!NOMEMP(symbol)){
+           OBJ_ATOM(symbol)->interned = TRUE;
+            HashTable_add(symbols, atom_name(symbol), get_ptr(symbol));
+        }
+    }
+    return symbol;
+}
diff --git a/tools/lib/sxpr.h b/tools/lib/sxpr.h
new file mode 100644 (file)
index 0000000..b900831
--- /dev/null
@@ -0,0 +1,413 @@
+/*
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of the
+ * License, or  (at your option) any later version. This library is 
+ * distributed in the  hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+#ifndef _XEN_LIB_SXPR_H_
+#define _XEN_LIB_SXPR_H_
+
+#include <stdint.h>
+
+#include "hash_table.h"
+#include "iostream.h"
+#include "allocate.h"
+
+/** @file
+ * Definitions for rules and sxprs.
+ */
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+/** Sxpr type. */
+typedef int16_t TypeCode;
+
+/** A typed sxpr handle.*/
+typedef struct Sxpr {
+    /** Sxpr type. */
+    TypeCode type;
+    union {
+       /** Sxpr value. */
+        unsigned long ul;
+       /** Pointer. */
+        void *ptr;
+    } v;
+} Sxpr;
+
+/** Sxpr type to indicate out of memory. */
+#define T_NOMEM      ((TypeCode)-1)
+/** The 'unspecified' sxpr. */
+#define T_NONE       ((TypeCode)0)
+/** The empty list. */
+#define T_NULL       ((TypeCode)1)
+/** Unsigned integer. */
+#define T_UINT       ((TypeCode)2)
+/** A string. */
+#define T_STRING     ((TypeCode)3)
+/** An atom. */
+#define T_ATOM       ((TypeCode)4)
+/** A boolean. */
+#define T_BOOL       ((TypeCode)5)
+
+/** A cons (pair or list). */
+#define T_CONS       ((TypeCode)10)
+
+/** An error. */
+#define T_ERR        ((TypeCode)40)
+
+/** An atom. */
+typedef struct ObjAtom {
+    Sxpr name;
+    Hashcode hashcode;
+    int interned;
+} ObjAtom;
+
+/** A cons (pair). */
+typedef struct ObjCons {
+    Sxpr car;
+    Sxpr cdr;
+} ObjCons;
+
+/** A vector. */
+typedef struct ObjVector {
+    int n;
+    Sxpr data[0];
+} ObjVector;
+
+/** Flags for sxpr printing. */
+enum PrintFlags {
+    PRINT_RAW           = 0x001,
+    PRINT_TYPE          = 0x002,
+    PRINT_PRETTY        = 0x004,
+    PRINT_NUM           = 0x008,
+};
+
+/** An integer sxpr.
+ *
+ * @param ty type
+ * @param val integer value
+ */
+#define OBJI(ty, val) (Sxpr){ type: (ty), v: { ul: (val) }}
+
+/** A pointer sxpr.
+ * If the pointer is non-null, returns an sxpr containing it.
+ * If the pointer is null, returns ONOMEM.
+ *
+ * @param ty type
+ * @param val pointer
+ */
+#define OBJP(ty, val) ((val) ? (Sxpr){ type: (ty), v: { ptr: (val) }} : ONOMEM)
+
+/** Make an integer sxpr containing a pointer.
+ *
+ * @param val pointer
+ */
+#define PTR(val) OBJP(T_UINT, (void*)(val))
+
+/** Make an integer sxpr.
+ * @param x value
+ */
+#define OINT(x)       OBJI(T_UINT,  x)
+
+/** Make an error sxpr.
+ *
+ * @param x value
+ */
+#define OERR(x)       OBJI(T_ERR,   x)
+
+/** Out of memory constant. */
+#define ONOMEM        OBJI(T_NOMEM, 0)
+
+/** The `unspecified' constant. */
+#define ONONE         OBJI(T_NONE,  0)
+
+/** Empty list constant. */
+#define ONULL         OBJI(T_NULL,  0)
+
+/** False constant. */
+#define OFALSE        OBJI(T_BOOL,  0)
+
+/** True constant. */
+#define OTRUE         OBJI(T_BOOL,  1)
+
+/* Recognizers for the various sxpr types.  */
+#define ATOMP(obj)        has_type(obj, T_ATOM)
+#define BOOLP(obj)        has_type(obj, T_BOOL)
+#define CONSP(obj)        has_type(obj, T_CONS)
+#define ERRP(obj)         has_type(obj, T_ERR)
+#define INTP(obj)         has_type(obj, T_UINT)
+#define NOMEMP(obj)       has_type(obj, T_NOMEM)
+#define NONEP(obj)        has_type(obj, T_NONE)
+#define NULLP(obj)        has_type(obj, T_NULL)
+#define STRINGP(obj)      has_type(obj, T_STRING)
+
+#define TRUEP(obj)    get_ul(obj)
+
+/** Convert an sxpr to an unsigned integer. */
+#define OBJ_UINT(x)   get_ul(x)
+/** Convert an sxpr to an integer. */
+#define OBJ_INT(x)    (int)get_ul(x)
+
+/* Conversions of sxprs to their values.
+ * No checking is done.
+ */
+#define OBJ_STRING(x)  ((char*)get_ptr(x))
+#define OBJ_CONS(x)    ((ObjCons*)get_ptr(x))
+#define OBJ_ATOM(x)    ((ObjAtom*)get_ptr(x))
+#define OBJ_SET(x)     ((ObjSet*)get_ptr(x))
+#define CAR(x)         (OBJ_CONS(x)->car)
+#define CDR(x)         (OBJ_CONS(x)->cdr)
+
+#define CAAR(x)        (CAR(CAR(x)))
+#define CADR(x)        (CAR(CDR(x)))
+#define CDAR(x)        (CDR(CAR(x)))
+#define CDDR(x)        (CDR(CDR(x)))
+
+/** Get the integer value from an sxpr.
+ *
+ * @param obj sxpr
+ * @return value
+ */
+static inline unsigned long get_ul(Sxpr obj){
+    return obj.v.ul;
+}
+
+/** Get the pointer value from an sxpr.
+ *
+ * @param obj sxpr
+ * @return value
+ */
+static inline void * get_ptr(Sxpr obj){
+    return obj.v.ptr;
+}
+
+/** Create an sxpr containing a pointer.
+ *
+ * @param type typecode
+ * @param val pointer
+ * @return sxpr
+ */
+static inline Sxpr obj_ptr(TypeCode type, void *val){
+    return (Sxpr){ type: type, v: { ptr: val } };
+}
+
+/** Create an sxpr containing an integer.
+ *
+ * @param type typecode
+ * @param val integer
+ * @return sxpr
+ */
+static inline Sxpr obj_ul(TypeCode type, unsigned long val){
+    return (Sxpr){ type: type, v: { ul: val } };
+}
+
+/** Get the type of an sxpr.
+ *
+ * @param obj sxpr
+ * @return type
+ */
+static inline TypeCode get_type(Sxpr obj){
+    return obj.type;
+}
+
+/** Check the type of an sxpr.
+ *
+ * @param obj sxpr
+ * @param type to check
+ * @return 1 if has the type, 0 otherwise
+ */
+static inline int has_type(Sxpr obj, TypeCode type){
+    return get_type(obj) == type;
+}
+
+/** Compare sxprs for literal equality of type and value.
+ *
+ * @param x sxpr to compare
+ * @param y sxpr to compare
+ * @return 1 if equal, 0 otherwise
+ */
+static inline int eq(Sxpr x, Sxpr y){
+    return ((get_type(x) == get_type(y)) && (get_ul(x) == get_ul(y)));
+}
+
+/** Checked version of CAR
+ *
+ * @param x sxpr
+ * @return CAR if a cons, x otherwise
+ */
+static inline Sxpr car(Sxpr x){
+    return (CONSP(x) ? CAR(x) : x);
+}
+
+/** Checked version of CDR.
+ *
+ * @param x sxpr
+ * @return CDR if a cons, null otherwise
+ */
+static inline Sxpr cdr(Sxpr x){
+    return (CONSP(x) ? CDR(x) : ONULL);
+}
+
+/** Allocate some memory and return an sxpr containing it.
+ * Returns ONOMEM if allocation failed.
+ *
+ * @param n number of bytes to allocate
+ * @param ty typecode
+ * @return sxpr
+ */
+static inline Sxpr halloc(size_t n,  TypeCode ty){
+    return OBJP(ty, allocate(n));
+}
+
+/** Allocate an sxpr containing a pointer to the given type.
+ *
+ * @param ty type (uses sizeof to determine how many bytes to allocate)
+ * @param code typecode
+ * @return sxpr, ONOMEM if allocation failed
+ */
+#define HALLOC(ty, code) halloc(sizeof(ty), code)
+
+typedef int ObjPrintFn(IOStream *io, Sxpr obj, unsigned flags);
+typedef int ObjEqualFn(Sxpr obj, Sxpr other);
+typedef void ObjFreeFn(Sxpr obj);
+
+/** An sxpr type definition. */
+typedef struct SxprType {
+    TypeCode type;
+    char *name;
+    int pointer;
+    ObjPrintFn *print;
+    ObjEqualFn *equal;
+    ObjFreeFn *free;
+} SxprType;
+
+
+extern SxprType *get_sxpr_type(int ty);
+
+/** Free the pointer in an sxpr.
+ *
+ * @param x sxpr containing a pointer
+ */
+static inline void hfree(Sxpr x){
+    deallocate(get_ptr(x));
+}
+
+extern int objprint(IOStream *io, Sxpr x, unsigned flags);
+extern int objequal(Sxpr x, Sxpr y);
+extern void objfree(Sxpr x);
+
+extern void cons_free_cells(Sxpr obj);
+extern Sxpr intern(char *s);
+
+extern Sxpr assoc(Sxpr k, Sxpr l);
+extern Sxpr assocq(Sxpr k, Sxpr l);
+extern Sxpr acons(Sxpr k, Sxpr v, Sxpr l);
+extern Sxpr nrev(Sxpr l);
+extern Sxpr cons_member(Sxpr l, Sxpr x);
+extern Sxpr cons_member_if(Sxpr l, ObjEqualFn *test_fn, Sxpr v);
+extern int cons_subset(Sxpr s, Sxpr t);
+extern int cons_set_equal(Sxpr s, Sxpr t);
+
+#ifdef USE_GC
+extern Sxpr cons_remove(Sxpr l, Sxpr x);
+extern Sxpr cons_remove_if(Sxpr l, ObjEqualFn *test_fn, Sxpr v);
+#endif
+
+extern Sxpr atom_new(char *name);
+extern char * atom_name(Sxpr obj);
+
+extern Sxpr string_new(char *s);
+extern char * string_string(Sxpr obj);
+extern int string_length(Sxpr obj);
+
+extern Sxpr cons_new(Sxpr car, Sxpr cdr);
+extern int cons_push(Sxpr *list, Sxpr elt);
+extern int cons_length(Sxpr obj);
+
+Sxpr sxpr_name(Sxpr obj);
+int sxpr_is(Sxpr obj, char *s);
+int sxpr_elementp(Sxpr obj, Sxpr name);
+Sxpr sxpr_attributes(Sxpr obj);
+Sxpr sxpr_attribute(Sxpr obj, Sxpr key, Sxpr def);
+Sxpr sxpr_children(Sxpr obj);
+Sxpr sxpr_child(Sxpr obj, Sxpr name, Sxpr def);
+Sxpr sxpr_child0(Sxpr obj, Sxpr def);
+Sxpr sxpr_child_value(Sxpr obj, Sxpr name, Sxpr def);
+
+/** Create a new atom.
+ *
+ * @param s atom name
+ * @return new atom
+ */
+static inline Sxpr mkatom(char *s){
+    return atom_new(s);
+}
+
+/** Create a new string sxpr.
+ *
+ * @param s string bytes (copied)
+ * @return new string
+ */
+static inline Sxpr mkstring(char *s){
+    return string_new(s);
+}
+
+/** Create an integer sxpr.
+ *
+ * @param i value
+ * @return sxpr
+ */
+static inline Sxpr mkint(int i){
+    return OBJI(T_UINT, i);
+}
+
+/** Create a boolean sxpr.
+ *
+ * @param b value
+ * @return sxpr
+ */
+static inline Sxpr mkbool(int b){
+    return OBJI(T_BOOL, (b ? 1 : 0));
+}
+
+/* Constants used in parsing and printing. */
+#define k_list_open    "("
+#define c_list_open    '('
+#define k_list_close   ")"
+#define c_list_close   ')'
+#define k_true         "true"
+#define k_false        "false"
+
+#define c_var          '$'
+#define c_escape       '\\'
+#define c_single_quote '\''
+#define c_double_quote '"'
+#define c_string_open  c_double_quote
+#define c_string_close c_double_quote
+#define c_data_open    '['
+#define c_data_close   ']'
+#define c_binary       '*'
+#define c_eval         '!'
+#define c_concat_open  '{'
+#define c_concat_close '}'
+
+#endif /* ! _XEN_LIB_SXPR_H_ */
diff --git a/tools/lib/sxpr_parser.c b/tools/lib/sxpr_parser.c
new file mode 100644 (file)
index 0000000..16fec56
--- /dev/null
@@ -0,0 +1,897 @@
+
+#ifdef __KERNEL__
+#  include <linux/config.h>
+#  include <linux/module.h>
+#  include <linux/kernel.h>
+#  include <linux/string.h>
+#  include <linux/errno.h>
+#else
+#  include <stdlib.h>
+#  include <errno.h>
+#endif
+
+#include "iostream.h"
+#include "lexis.h"
+#include "sxpr_parser.h"
+#include "sys_string.h"
+
+/** @file
+ * Sxpr parsing.
+ *
+ * So that the parser does not leak memory, all sxprs constructed by
+ * the parser must be freed on error.  On successful parse the sxpr
+ * returned becomes the responsibility of the caller.
+ *
+ * @author Mike Wray <mike.wray@hpl.hp.com>
+ */
+
+#define dprintf(fmt, args...) IOStream_print(iostdout, "[DEBUG] %s" fmt, __FUNCTION__, ##args)
+#define printf(fmt, args...)   IOStream_print(iostdout, fmt, ##args)
+
+static void reset(Parser *z);
+static int inputchar(Parser *p, char c);
+static int savechar(Parser *p, char c);
+extern void parse_error(Parser *in);
+extern void parse_error_id(Parser *in, ParseErrorId id);
+
+static int begin_start(Parser *p, char c);
+static int state_start(Parser *p, char c);
+static int end_start(Parser *p);
+
+static int begin_comment(Parser *p, char c);
+static int state_comment(Parser *p, char c);
+static int end_comment(Parser *p);
+
+static int begin_string(Parser *p, char c);
+static int state_string(Parser *p, char c);
+static int end_string(Parser *p);
+static int state_escape(Parser *p, char c);
+static int state_octal(Parser *p, char c);
+static int state_hex(Parser *p, char c);
+
+static int begin_atom(Parser *p, char c);
+static int state_atom(Parser *p, char c);
+static int end_atom(Parser *p);
+
+static int state_list(Parser *p, char c);
+static int begin_list(Parser *p, char c);
+static int end_list(Parser *p);
+
+/** Print a parse error.
+ *
+ * @param in parser
+ * @param msg format followed by printf arguments
+ */
+void eprintf(Parser *in, char *msg, ...){
+    va_list args;
+    if(in->error_out){
+        va_start(args, msg);
+        IOStream_vprint(in->error_out, msg, args);
+        va_end(args);
+    }
+}
+
+/** Print a parse warning.
+ *
+ * @param in parser
+ * @param msg format followed by printf arguments
+ */
+void wprintf(Parser *in, char *msg, ...){
+    va_list args;
+    if(in->error_out){
+        va_start(args, msg);
+        IOStream_vprint(in->error_out, msg, args);
+        va_end(args);
+    }
+}
+
+/*============================================================================*/
+
+/** Record defining the message for a parse error. */
+typedef struct {
+  ParseErrorId id;
+  char *message;
+} ParseError;
+
+/** Format for printing parse error messages. */
+#define PARSE_ERR_FMT "parse error> line %3d, column %2d: %s"
+
+/** Message catalog for the parse error codes. */
+static ParseError catalog[] = {
+  { PARSE_ERR_UNSPECIFIED,            "unspecified error" },
+  { PARSE_ERR_NOMEM,                  "out of memory" },
+  { PARSE_ERR_UNEXPECTED_EOF,         "unexpected end of input" },
+  { PARSE_ERR_TOKEN_TOO_LONG,         "token too long" },
+  { PARSE_ERR_INVALID_SYNTAX,         "syntax error" },
+  { PARSE_ERR_INVALID_ESCAPE,         "invalid escape" },
+  { 0, NULL }
+};
+
+/** Number of entries in the message catalog. */
+const static int catalog_n = sizeof(catalog)/sizeof(ParseError);
+
+void ParserState_free(ParserState *z){
+    if(!z) return;
+    objfree(z->val);
+    deallocate(z);
+}
+
+int ParserState_new(ParserStateFn *fn, ParserState *parent, ParserState **val){
+    int err = 0;
+    ParserState *z;
+    z = ALLOCATE(ParserState);
+    if(z){
+        z->fn = fn;
+        z->parent = parent;
+        z->val = ONULL;
+    } else {
+        err = -ENOMEM;
+    }
+    if(!err) *val = z;
+    return err;
+}
+
+/** Free a parser.
+ * No-op if the parser is null.
+ *
+ * @param z parser 
+ */
+void Parser_free(Parser *z){
+    if(!z) return;
+    objfree(z->val);
+    z->val = ONONE;
+    deallocate(z);
+}
+
+/** Create a new parser. The error stream defaults to null.
+ */
+Parser * Parser_new(void){
+    Parser *z = ALLOCATE(Parser);
+    int err = -ENOMEM;
+  
+    if(!z) goto exit;
+    err = 0;
+    reset(z);
+  exit:
+    if(err){
+        Parser_free(z);
+        z = NULL;
+    }
+    return z;
+}
+
+/** Get the next character.
+ * Records the character read in the parser,
+ * and sets the line and character counts.
+ *
+ * @param p parser
+ * @return error flag: 0 on success, non-zero on error
+ */
+static int inputchar(Parser *p, char c){
+    int err = 0;
+    if(c=='\n'){
+        p->line_no++;
+        p->char_no = 0;
+    } else {
+        p->char_no++;
+    }
+    return err;
+}
+
+static int savechar(Parser *p, char c){
+    int err = 0;
+    if(p->buf_i >= p->buf_n){
+        err = -ENOMEM;
+        goto exit;
+    }
+    p->buf[p->buf_i] = c;
+    p->buf_i++;
+  exit:
+    return err;
+}
+
+int Parser_input_char(Parser *p, char c){
+    int err = 0;
+    if(at_eof(p)){
+        //skip;
+    } else {
+        inputchar(p, c);
+    }
+    if(!p->state){
+        err = begin_start(p, c);
+        if(err) goto exit;
+    }
+    err = p->state->fn(p, c);
+  exit:
+    return err;
+}
+
+int Parser_input_eof(Parser *p){
+    int err = 0;
+    p->eof = 1;
+    err = Parser_input_char(p, IOSTREAM_EOF);
+    return err;
+}
+
+int Parser_input(Parser *p, char *buf, int buf_n){
+    int err = 0;
+    int i = 0;
+    if(buf_n <= 0){
+        err = Parser_input_eof(p);
+        goto exit;
+    }
+    for(i = 0; i<buf_n; i++){
+        err = Parser_input_char(p, buf[i]);
+        if(err) goto exit;
+    }
+  exit:
+    err = (err < 0 ? err : buf_n);
+    return err;
+}
+
+int Parser_push(Parser *p, ParserStateFn *fn){
+    int err = 0;
+    err = ParserState_new(fn, p->state, &p->state);
+    return err;
+}
+        
+int Parser_pop(Parser *p){
+    int err = 0;
+    ParserState *s = p->state;
+    p->state = s->parent;
+    ParserState_free(s);
+    return err;
+}
+
+int Parser_return(Parser *p){
+    int err = 0;
+    Sxpr val = ONONE;
+    if(!p->state){
+        err = -EINVAL;
+        goto exit;
+    }
+    val = p->state->val;
+    p->state->val = ONONE;
+    err = Parser_pop(p);
+    if(err) goto exit;
+    if(p->state){
+        err = cons_push(&p->state->val, val);
+    } else {
+        val = nrev(val);
+        p->val = val;
+    }
+  exit:
+    if(err){
+        objfree(val);
+    }
+    return err;
+}
+
+/** Determine if a character is a separator.
+ *
+ * @param p parser
+ * @param c character to test
+ * @return 1 if a separator, 0 otherwise
+ */
+static int is_separator(Parser *p, char c){
+    return in_sep_class(c);
+}
+
+/** Return the current token.
+ * The return value points at the internal buffer, so
+ * it must not be modified (or freed). Use copy_token() if you need a copy.
+ *
+ * @param p parser
+ * @return token
+ */
+char *peek_token(Parser *p){
+    return p->buf;
+}
+
+/** Return a copy of the current token.
+ * The returned value should be freed when finished with.
+ *
+ * @param p parser
+ * @return copy of token
+ */
+char *copy_token(Parser *p){
+    return strdup(peek_token(p));
+}
+
+static int do_intern(Parser *p){
+    int err = 0;
+    Sxpr obj = intern(peek_token(p));
+    if(NOMEMP(obj)){
+        err = -ENOMEM;
+    } else {
+        p->state->val = obj;
+    }
+    return err;
+}
+
+static int do_string(Parser *p){
+    int err = 0;
+    Sxpr obj;
+    obj = string_new(peek_token(p));
+    if(NOMEMP(obj)){
+        err = -ENOMEM;
+    } else {
+        p->state->val = obj;
+    }
+    return err;
+}
+
+void newtoken(Parser *p){
+    memset(p->buf, 0, p->buf_n);
+    p->buf_i = 0;
+    p->tok_begin_line = p->line_no;
+    p->tok_begin_char = p->char_no;
+}
+
+int get_escape(char c, char *d){
+    int err = 0;
+    switch(c){
+    case 'a':            *d = '\a'; break;
+    case 'b':            *d = '\b'; break;
+    case 'f':            *d = '\f'; break;
+    case 'n':            *d = '\n'; break;
+    case 'r':            *d = '\r'; break;
+    case 't':            *d = '\t'; break;
+    case 'v':            *d = '\v'; break;
+    case c_escape:       *d = c_escape; break;
+    case c_single_quote: *d = c_single_quote; break;
+    case c_double_quote: *d = c_double_quote; break;
+    default:
+        err = -EINVAL;
+    }
+    return err;
+}
+
+
+int begin_start(Parser *p, char c){
+    return Parser_push(p, state_start);
+}
+
+int state_start(Parser *p, char c){
+    int err = 0;
+    if(at_eof(p)){
+        err = end_start(p);
+    } else if(in_space_class(c)){
+        //skip
+    } else if(in_comment_class(c)){
+        begin_comment(p, c);
+    } else if(c == c_list_open){
+        begin_list(p, c);
+    } else if(c == c_list_close){
+        parse_error(p);
+        err = -EINVAL;
+    } else if(in_string_quote_class(c)){
+        begin_string(p, c);
+    } else if(in_printable_class(c)){
+        begin_atom(p, c);
+    } else if(c == 0x04){
+        //ctrl-D, EOT: end-of-text.
+        Parser_input_eof(p);
+    } else {
+        parse_error(p);
+        err = -EINVAL;
+    }
+    return err;
+}
+
+int end_start(Parser *p){
+    int err = 0;
+    err = Parser_return(p);
+    return err;
+}
+
+int begin_comment(Parser *p, char c){
+    int err = 0;
+    err = Parser_push(p, state_comment);
+    if(err) goto exit;
+    err = inputchar(p, c);
+  exit:
+    return err;
+}
+
+int state_comment(Parser *p, char c){
+    int err = 0;
+    if(c == '\n' || at_eof(p)){
+        err = end_comment(p);
+    } else {
+        err = inputchar(p, c);
+    }
+    return err;
+}
+
+int end_comment(Parser *p){
+    return Parser_pop(p);
+}
+
+int begin_string(Parser *p, char c){
+    int err = 0;
+    err = Parser_push(p, state_string);
+    if(err) goto exit;
+    newtoken(p);
+    p->state->delim = c;
+  exit:
+    return err;
+}
+
+int state_string(Parser *p, char c){
+    int err = 0;
+    if(at_eof(p)){
+        parse_error_id(p, PARSE_ERR_UNEXPECTED_EOF);
+        err = -EINVAL;
+    } else if(c == p->state->delim){
+        err = end_string(p);
+    } else if(c == '\\'){
+        err = Parser_push(p, state_escape);
+    } else {
+        err = savechar(p, c);
+    }
+    return err;
+}
+
+int end_string(Parser *p){
+    int err = 0;
+    err = do_string(p);
+    if(err) goto exit;
+    err = Parser_return(p);
+  exit:
+    return err;
+}
+
+int state_escape(Parser *p, char c){
+    int err = 0;
+    char d;
+    if(at_eof(p)){
+        parse_error_id(p, PARSE_ERR_UNEXPECTED_EOF);
+        err = -EINVAL;
+        goto exit;
+    }
+    if(get_escape(c, &d) == 0){
+        err = savechar(p, d);
+        if(err) goto exit;
+        err = Parser_pop(p);
+    } else if(c == 'x'){
+        p->state->fn = state_hex;
+        p->state->ival = 0;
+        p->state->count = 0;
+    } else {
+        p->state->fn = state_octal;
+        p->state->ival = 0;
+        p->state->count = 0;
+        err = Parser_input_char(p, c);
+    }
+  exit:
+    return err;
+}
+
+int octaldone(Parser *p){
+    int err = 0;
+    char d = (char)(p->state->ival & 0xff);
+    err = Parser_pop(p);
+    if(err) goto exit;
+    err = Parser_input_char(p, d);
+  exit:
+    return err;
+}
+
+int octaldigit(Parser *p, char c){
+    int err = 0;
+    p->state->ival *= 8;
+    p->state->ival += c - '0'; 
+    p->state->count++;
+    if(err) goto exit;
+    if(p->state->ival < 0 || p->state->ival > 0xff){
+        parse_error(p);
+        err = -EINVAL;
+        goto exit;
+    }
+    if(p->state->count == 3){
+        err = octaldone(p);
+    }
+  exit:
+    return err;
+}
+
+int state_octal(Parser *p, char c){
+    int err = 0;
+    if(at_eof(p)){
+        parse_error_id(p, PARSE_ERR_UNEXPECTED_EOF);
+        err = -EINVAL;
+        goto exit;
+    } else if('0' <= c && c <= '7'){
+        err = octaldigit(p, c);
+    } else {
+        err = octaldone(p);
+        if(err) goto exit;
+        Parser_input_char(p, c);
+    }
+  exit:
+    return err;
+}
+
+int hexdone(Parser *p){
+    int err = 0;
+    char d = (char)(p->state->ival & 0xff);
+    err = Parser_pop(p);
+    if(err) goto exit;
+    err = Parser_input_char(p, d);
+  exit:
+    return err;
+}
+    
+int hexdigit(Parser *p, char c, char d){
+    int err = 0;
+    p->state->ival *= 16;
+    p->state->ival += c - d; 
+    p->state->count++;
+    if(err) goto exit;
+    if(p->state->ival < 0 || p->state->ival > 0xff){
+        parse_error(p);
+        err = -EINVAL;
+        goto exit;
+    }
+    if(p->state->count == 2){
+        err = hexdone(p);
+    }
+  exit:
+    return err;
+}
+    
+int state_hex(Parser *p, char c){
+    int err = 0;
+    if(at_eof(p)){
+        parse_error_id(p, PARSE_ERR_UNEXPECTED_EOF);
+        err = -EINVAL;
+        goto exit;
+    } else if('0' <= c && c <= '9'){
+        err = hexdigit(p, c, '0');
+    } else if('A' <= c && c <= 'F'){
+        err = hexdigit(p, c, 'A');
+    } else if('a' <= c && c <= 'f'){
+        err = hexdigit(p, c, 'a');
+    } else if(p->state->count){
+        err =hexdone(p);
+        if(err) goto exit;
+        Parser_input_char(p, c);
+    }
+  exit:
+    return err;
+}
+
+int begin_atom(Parser *p, char c){
+    int err = 0;
+    err = Parser_push(p, state_atom);
+    if(err) goto exit;
+    newtoken(p);
+    err = savechar(p, c);
+  exit:
+    return err;
+}
+
+int state_atom(Parser *p, char c){
+    int err = 0;
+    if(at_eof(p)){
+        err = end_atom(p);
+    } else if(is_separator(p, c) ||
+              in_space_class(c) ||
+              in_comment_class(c)){
+        err = end_atom(p);
+        if(err) goto exit;
+        err = Parser_input_char(p, c);
+    } else {
+        err = savechar(p, c);
+    }
+  exit:
+    return err;
+}
+
+int end_atom(Parser *p){
+    int err = 0;
+    err = do_intern(p);
+    if(err) goto exit;
+    err = Parser_return(p);
+  exit:
+    return err;
+}
+
+int state_list(Parser *p, char c){
+    int err = 0;
+    if(at_eof(p)){
+        parse_error_id(p, PARSE_ERR_UNEXPECTED_EOF);
+        err = -EINVAL;
+    } else if(c == c_list_close){
+        p->state->val = nrev(p->state->val);
+        err = end_list(p);
+    } else {
+        err = state_start(p, c);
+    }
+    return err;
+    
+}
+
+int begin_list(Parser *p, char c){
+    return Parser_push(p, state_list);
+}
+
+int end_list(Parser *p){
+    return Parser_return(p);
+}
+
+/** Reset the fields of a parser to initial values.
+ *
+ * @param z parser
+ */
+static void reset(Parser *z){
+  IOStream *error_out = z->error_out;
+  int flags = z->flags;
+  zero(z, sizeof(Parser));
+  z->buf_n = sizeof(z->buf) - 1;
+  z->buf_i = 0;
+  z->line_no = 1;
+  z->char_no = 0;
+  z->error_out = error_out;
+  z->flags = flags;
+}
+
+/** Set the parser error stream.
+ * Parse errors are reported on the the error stream if it is non-null.
+ * 
+ * @param z parser
+ * @param error_out error stream
+ */
+void set_error_stream(Parser *z, IOStream *error_out){
+  if(z){
+    z->error_out = error_out;
+  }
+}
+
+/** Get the parser error message for an error code.
+ *
+ * @param id error code
+ * @return error message (empty string if the code is unknown)
+ */
+static char *get_message(ParseErrorId id){
+  int i;
+  for(i=0; i<catalog_n; i++){
+    if(id == catalog[i].id){
+      return catalog[i].message;
+    }
+  }
+  return "";
+}
+
+/** Get the line number.
+ *
+ * @param in parser
+ */
+int get_line(Parser *in){
+  return in->line_no;
+}
+
+/** Get the column number.
+ *
+ * @param in parser
+ */
+int get_column(Parser *in){
+  return in->char_no;
+}
+
+/** Get the line number the current token started on.
+ *
+ * @param in parser
+ */
+int get_tok_line(Parser *in){
+  return in->tok_begin_line;
+}
+
+/** Get the column number the current token started on.
+ *
+ * @param in parser
+ */
+int get_tok_column(Parser *in){
+  return in->tok_begin_char;
+}
+
+/** Report a parse error.
+ * Does nothing if the error stream is null or there is no error.
+ *
+ * @param in parser
+ */
+static void report_error(Parser *in){
+  if(in->error_out && in->err){
+    char *msg = get_message(in->err);
+    char *tok = peek_token(in);
+    IOStream_print(in->error_out, PARSE_ERR_FMT,
+                  get_tok_line(in), get_tok_column(in), msg);
+    if(tok && tok[0]){
+        IOStream_print(in->error_out, " '%s'", tok);
+    }
+    IOStream_print(in->error_out, "\n");
+  }
+}
+
+/** Get the error message for the current parse error code.
+ * Does nothing if there is no error.
+ *
+ * @param in parser
+ * @param buf where to place the message
+ * @param n maximum number of characters to place in buf
+ * @return current error code (zero for no error)
+ */
+int parse_error_message(Parser *in, char *buf, int n){
+    if(in->err){
+        char *msg = get_message(in->err);
+        snprintf(buf, n, PARSE_ERR_FMT, get_tok_line(in), get_tok_column(in), msg);
+    }
+    return in->err;
+}
+
+/** Flag an unspecified parse error. All subsequent reads will fail.
+ *
+ * @param in parser
+ */
+void parse_error(Parser *in){
+    parse_error_id(in, PARSE_ERR_INVALID_SYNTAX);
+}
+
+/** Flag a parse error. All subsequent reads will fail.
+ * Does not change the parser error code if it is already set.
+ *
+ * @param in parser
+ * @param id error code
+ */
+void parse_error_id(Parser *in, ParseErrorId id){
+    if(!in->err){
+        in->err = id;
+        report_error(in);
+    }
+}
+
+/** Test if the parser's error flag is set.
+ *
+ * @param in parser
+ * @return 1 if set, 0 otherwise
+ */
+int has_error(Parser *in){
+    return (in->err > 0);
+}
+
+/** Test if the parser is at end of input.
+ *
+ * @param in parser
+ * @return 1 if at EOF, 0 otherwise
+ */
+int at_eof(Parser *p){
+    return p->eof;
+}
+
+#ifdef SXPR_PARSER_MAIN
+/* Stuff for standalone testing. */
+
+#include "file_stream.h"
+#include "string_stream.h"
+
+int stringof(Sxpr exp, char **s){
+    int err = 0;
+    if(ATOMP(exp)){
+        *s = atom_name(exp);
+    } else if(STRINGP(exp)){
+        *s = string_string(exp);
+    } else {
+        err = -EINVAL;
+        *s = NULL;
+    }
+    return err;
+}
+
+int child_string(Sxpr exp, Sxpr key, char **s){
+    int err = 0;
+    Sxpr val = sxpr_child_value(exp, key, ONONE);
+    err = stringof(val, s);
+    return err;
+}
+
+int intof(Sxpr exp, int *v){
+    int err = 0;
+    char *s;
+    unsigned long l;
+    if(INTP(exp)){
+        *v = OBJ_INT(exp);
+    } else {
+        err = stringof(exp, &s);
+        if(err) goto exit;
+        err = convert_atoul(s, &l);
+        *v = (int)l;
+    }
+ exit:
+    return err;
+}
+
+int child_int(Sxpr exp, Sxpr key, int *v){
+    int err = 0;
+    Sxpr val = sxpr_child_value(exp, key, ONONE);
+    err = intof(val, v);
+    return err;
+}
+
+int eval_vnet(Sxpr exp){
+    int err = 0;
+    Sxpr oid = intern("id");
+    int id;
+    err = child_int(exp, oid, &id);
+    if(err) goto exit;
+    dprintf("> vnet id=%d\n", id);
+ exit:
+    dprintf("< err=%d\n", err);
+    return err;
+}
+
+int eval_connect(Sxpr exp){
+    int err = 0;
+    Sxpr ovif = intern("vif");
+    Sxpr ovnet = intern("vnet");
+    char *vif;
+    int vnet;
+
+    err = child_string(exp, ovif, &vif);
+    if(err) goto exit;
+    err = child_int(exp, ovnet, &vnet);
+    if(err) goto exit;
+    dprintf("> connect vif=%s vnet=%d\n", vif, vnet);
+ exit:
+    dprintf("< err=%d\n", err);
+    return err;
+}
+
+int eval(Sxpr exp){
+    int err = 0;
+    Sxpr oconnect = intern("connect");
+    Sxpr ovnet = intern("vnet");
+    
+    if(sxpr_elementp(exp, ovnet)){
+        err = eval_vnet(exp);
+    } else if(sxpr_elementp(exp, oconnect)){
+        err = eval_connect(exp);
+    } else {
+        err = -EINVAL;
+    }
+    return err;
+}
+
+/** Main program for testing.
+ * Parses input and prints it.
+ *
+ * @param argc number of arguments
+ * @param argv arguments
+ * @return error code
+ */
+int main(int argc, char *argv[]){
+    Parser *pin;
+    int err = 0;
+    char buf[1024];
+    int k;
+    Sxpr obj, l, x;
+
+    pin = Parser_new();
+    set_error_stream(pin, iostdout);
+    dprintf("> parse...\n");
+    while(1){
+        k = fread(buf, 1, 1024, stdin);
+        err = Parser_input(pin, buf, k);
+        dprintf("> Parser_input=%d\n", err);
+        if(k <= 0) break;
+    }
+    obj = pin->val;
+    for(l = obj ; CONSP(l); l = CDR(l)){
+        x = CAR(l);
+        objprint(iostdout, x, 0); printf("\n");
+        eval(x);
+    }
+    dprintf("> err=%d\n", err);
+    return 0;
+}
+#endif
diff --git a/tools/lib/sxpr_parser.h b/tools/lib/sxpr_parser.h
new file mode 100644 (file)
index 0000000..7296312
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of the
+ * License, or  (at your option) any later version. This library is 
+ * distributed in the  hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#ifndef _XEN_LIB_SXPR_PARSER_H_
+#define _XEN_LIB_SXPR_PARSER_H_
+
+#include "sxpr.h"
+#include "iostream.h"
+
+/** @file
+ * Sxpr parsing definitions.
+ */
+
+/** Size of a parser input buffer.
+ * Tokens read must fit into this size (including trailing null).
+ */
+#define PARSER_BUF_SIZE 1024
+
+struct Parser;
+typedef int ParserStateFn(struct Parser *, char c);
+
+typedef struct ParserState {
+    struct ParserState *parent;
+    Sxpr val;
+    int ival;
+    int count;
+    char delim;
+    ParserStateFn *fn;
+} ParserState;
+
+/** Structure representing an input source for the parser.
+ * Can read from any IOStream implementation.
+ */
+typedef struct Parser {
+    Sxpr val;
+    /** Error reporting stream (null for no reports). */
+    IOStream *error_out;
+    int eof;
+    /** Error flag. Non-zero if there has been a read error. */
+    int err;
+    /** Line number on input (from 1). */
+    int line_no;
+    /** Column number of input (reset on new line). */
+    int char_no;
+    /** Lookahead character. */
+    char c;
+    /** Buffer for reading tokens. */
+    char buf[PARSER_BUF_SIZE];
+    /** Size of token buffer. */
+    int buf_n;
+    int buf_i;
+    /** Line the last token started on. */
+    int tok_begin_line;
+    /** Character number the last token started on. */
+    int tok_begin_char;
+    /** Parsing flags. */
+    int flags;
+    ParserState *state;
+} Parser;
+
+/** Parser error codes. */
+typedef enum {
+    PARSE_ERR_NONE=0,
+    PARSE_ERR_UNSPECIFIED,
+    PARSE_ERR_NOMEM,
+    PARSE_ERR_UNEXPECTED_EOF,
+    PARSE_ERR_TOKEN_TOO_LONG,
+    PARSE_ERR_INVALID_SYNTAX,
+    PARSE_ERR_INVALID_ESCAPE,
+} ParseErrorId;
+
+
+/** Parser flags. */
+//enum {
+//};
+
+/** Raise some parser flags.
+ *
+ * @param in parser
+ * @param flags flags mask
+ */
+inline static void parser_flags_raise(Parser *in, int flags){
+    in->flags |= flags;
+}
+
+/** Lower some parser flags.
+ *
+ * @param in parser
+ * @param flags flags mask
+ */
+inline static void parser_flags_lower(Parser *in, int flags){
+    in->flags &= ~flags;
+}
+
+/** Clear all parser flags.
+ *
+ * @param in parser
+ */
+inline static void parser_flags_clear(Parser *in){
+    in->flags = 0;
+}
+
+extern void Parser_free(Parser *z);
+extern Parser * Parser_new(void);
+extern int Parser_input(Parser *p, char *buf, int buf_n);
+extern int Parser_input_eof(Parser *p);
+
+extern int parse_error_message(Parser *in, char *buf, int n);
+extern int has_error(Parser *in);
+extern int at_eof(Parser *in);
+
+#endif /* ! _XEN_LIB_SXPR_PARSER_H_ */
diff --git a/tools/lib/sys_ctype.h b/tools/lib/sys_ctype.h
new file mode 100644 (file)
index 0000000..1dc6cf2
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef _XENO_SYS_CTYPE_H_
+#define _XENO_SYS_CTYPE_H_
+/** @file
+ ** Replacement for ctype include that can be used
+ * from user or kernel code.
+ */
+#ifdef __KERNEL__
+#  include <linux/ctype.h>
+#else
+#  include <ctype.h>
+#endif
+#endif /* ! _XENO_SYS_CTYPE_H_ */
diff --git a/tools/lib/sys_net.c b/tools/lib/sys_net.c
new file mode 100644 (file)
index 0000000..0e7ac5d
--- /dev/null
@@ -0,0 +1,309 @@
+/*
+ * Copyright (C) 2001 - 2004 Mike Wray <mike.wray@hp.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of the
+ * License, or  (at your option) any later version. This library is 
+ * distributed in the  hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include "sys_net.h"
+#include "sys_string.h"
+
+/** @file
+ * All network data are kept in network order and only converted to
+ * host order for display. Network data includes IP addresses, port numbers and
+ * network masks.
+ */
+
+/** Maximum value for a port. */
+#define PORT_MAX 0xffff
+
+/** Convert a number of bits to a network mask
+ * for IP addresses. The number of bits must
+ * be in the range 1-31.
+ *
+ * @param n number of bits to set in the mask
+ * @return value with n high bits set (in network order)
+ */
+unsigned long bits_to_mask(int n){
+    unsigned long mask = (n ? (1 << 31) : 0);
+    int i;
+    for(i=1; i<n; i++){
+        mask |= (mask >> 1);
+    }
+    return htonl(mask);
+}
+
+/** Convert a network mask to a number of bits.
+ *
+ * @param mask network mask in network order
+ * @return number of bits in mask
+ */
+int mask_to_bits(unsigned long mask){
+    // Start with n set to the number of bits in the mask. Then reduce n by
+    // the number of low zero bits in the mask.
+    int n = 32;
+    for(mask = ntohl(mask);
+        (mask & 1)==0 && n>0;
+        mask >>= 1){
+        n--;
+    }
+    return n;
+}
+
+/** Get the index of the first occurrence of a character in a string.
+ * Stops at end of string or after n characters.
+ *
+ * @param s input string
+ * @param n maximum number of charactes to search
+ * @param c character to look for
+ * @return index of first occurrence, -1 if not found
+ */
+inline static int indexof(const char *s, int n, char c){
+    int i;
+    for(i=0; i<n && *s; i++, s++){
+        if(*s == c) return i;
+    }
+    return -1;
+}
+
+/** Convert an IPv4 address in dot notation into an unsigned long (in network order).
+ *
+ * @param s input string
+ * @param address where to put the address
+ * @return 0 on success, -1 on error
+ */
+int get_inet_addr(const char *s, unsigned long *address){
+    // Number of bits in a byte.
+    const int BYTE_BITS = 8;
+    // Number of bytes in a word.
+    const int WORD_BYTES = 4;
+    // Max value for a component of an address.
+    const int ADDR_MAX  = 255;
+    // Separator for components of an address.
+    const char dot = '.';
+
+    int n;
+    unsigned long addr = 0;
+    unsigned long v;
+    int i;
+    int err = -1;
+    // Bit shift for the current byte.
+    int shift = BYTE_BITS * (WORD_BYTES - 1);
+    char buf[64];
+
+    n = strlen(s);
+    if(n >= sizeof(buf)){
+        goto exit;
+    }
+    for(i=0; i < WORD_BYTES; i++){
+        int idx = indexof(s, n, dot);
+        idx = (idx < 0 ? strlen(s) : idx);
+        strncpy(buf, s, idx); buf[idx]='\0';
+        if(convert_atoul(buf, &v)){
+            goto exit;
+        }
+        if(v < 0 || v > ADDR_MAX){
+            goto exit;
+        }
+        addr |= (v << shift);
+        if(idx == n) break;
+        shift -= BYTE_BITS;
+        s += idx+1;
+    }
+    err = 0;
+  exit:
+    addr = htonl(addr);
+    *address = (err ? 0 : addr);
+    return err;
+}
+
+#ifdef __KERNEL__
+/** Convert an address in network order to IPv4 dot notation.
+ * The return value is a static buffer which is overwritten on each call.
+ *
+ * @param inaddr address (in network order)
+ * @return address in dot notation
+ */
+char *inet_ntoa(struct in_addr inaddr){
+    static char address[16] = {};
+    uint32_t addr = ntohl(inaddr.s_addr);
+    snprintf(address, sizeof(address), "%d.%d.%d.%d",
+            (unsigned)((addr >> 24) & 0xff),
+            (unsigned)((addr >> 16) & 0xff),
+            (unsigned)((addr >>  8) & 0xff),
+            (unsigned)((addr      ) & 0xff));
+    return address;
+}
+
+
+/** Convert a string in IPv4 dot notation to an int in network order.
+ *
+ * @param address address in dot notation
+ * @param inp result of conversion (in network order)
+ * @return 0 on success, error code on error
+ */
+int inet_aton(const char *address, struct in_addr *inp){
+    int err = 0; 
+    unsigned long addr;
+    
+    err = get_inet_addr(address, &addr);
+    if(err) goto exit;
+    inp->s_addr = addr;
+  exit:
+    return err;
+}
+#endif
+
+/** Convert a hostname or IPv4 address string to an address in network order.
+ *
+ * @param name input hostname or address string
+ * @param address where to put the address
+ * @return 1 if address found OK, 0 otherwise
+ */
+int get_host_address(const char *name, unsigned long *address){
+#ifdef __KERNEL__
+    return get_inet_addr(name, address) == 0;
+#else
+    struct hostent *host = gethostbyname(name);
+    if(!host){
+        return 0;
+    }
+    *address = ((struct in_addr *)(host->h_addr))->s_addr;
+    return 1;
+#endif
+}
+
+/** Convert a service name to a port (in network order).
+ *
+ * @param name service name
+ * @param port where to put the port
+ * @return 1 if service port found OK, 0 otherwise
+ */
+int get_service_port(const char *name, unsigned long *port){
+#ifdef __KERNEL__
+    return 0;
+#else
+    struct servent *service;
+    service = getservbyname(name, 0);
+    if(!service){
+        return 0;
+    }
+    *port = service->s_port;
+    return 1;
+#endif
+}
+
+/** Convert a port number (in network order) to a service name.
+ *
+ * @param port the port number
+ * @return service name if found OK, 0 otherwise
+ */
+char *get_port_service(unsigned long port){
+#ifdef __KERNEL__
+    return 0;
+#else
+    struct servent *service = getservbyport(port, 0);
+    return (service ? service->s_name : 0);
+#endif
+}
+
+/** Convert a decimal integer or service name to a port (in network order).
+ *
+ * @param s input to convert
+ * @param port where to put the port
+ * @return 1 if port found OK, 0 otherwise
+ */
+int convert_service_to_port(const char *s, unsigned long *port){
+    int ok = 0;
+    unsigned long value;
+    if(convert_atoul(s, &value)){
+        ok = get_service_port(s, &value);
+    } else {
+        ok = (0 <= value) && (value <= PORT_MAX);
+        value = htons((unsigned short)value);
+    }
+    *port = (ok ? value : 0);
+    return ok;
+}
+
+#define MAC_ELEMENT_N  6 // Number of elements in a MAC address.
+#define MAC_DIGIT_N    2 // Number of digits in an element in a MAC address.
+#define MAC_LENGTH    17 //((MAC_ELEMENT_N * MAC_DIGIT_N) + MAC_ELEMENT_N - 1)
+
+/** Convert a mac address from a string of the form
+ * XX:XX:XX:XX:XX:XX to numerical form (an array of 6 unsigned chars).
+ * Each X denotes a hex digit: 0..9, a..f, A..F.
+ * Also supports using '-' as the separator instead of ':'.
+ *
+ * @param mac_in string to convert
+ * @param mac destination for the value
+ * @return 0 on success, -1 on error
+ */
+int mac_aton(const char *mac_in, unsigned char *mac){
+    int err = 0;
+    int i, j;
+    const char *p;
+    char sep = 0;
+    unsigned char d;
+    if(!mac_in || strlen(mac_in) != MAC_LENGTH){
+        err = -1;
+        goto exit;
+    }
+    for(i = 0, p = mac_in; i < MAC_ELEMENT_N; i++){
+        d = 0;
+        if(i){
+            if(!sep){
+                if(*p == ':' || *p == '-') sep = *p;
+            }
+            if(sep && *p == sep){
+                p++;
+            } else {
+                err = -1;
+                goto exit;
+            }
+        }
+        for(j = 0; j < MAC_DIGIT_N; j++, p++){
+            if(j) d <<= 4;
+            if(*p >= '0' && *p <= '9'){
+                d += (*p - '0');
+            } else if(*p >= 'A' && *p <= 'F'){
+                d += (*p - 'A') + 10;
+            } else if(*p >= 'a' && *p <= 'f'){
+                d += (*p - 'a') + 10;
+            } else {
+                err = -1;
+                goto exit;
+            }
+        }
+        mac[i] = d;
+    }
+  exit:
+    return err;
+}
+
+/** Convert a MAC address from numerical form to a string.
+ *
+ * @param mac address to convert
+ * @return static string value
+ */
+char *mac_ntoa(const unsigned char *mac){
+    static char buf[MAC_LENGTH + 1];
+    int buf_n = sizeof(buf);
+
+    memset(buf, buf_n, 0);
+    snprintf(buf, buf_n, "%02x:%02x:%02x:%02x:%02x:%02x",
+             mac[0], mac[1], mac[2],
+             mac[3], mac[4], mac[5]);
+    buf[buf_n - 1] = '\0';
+    return buf;
+}
diff --git a/tools/lib/sys_net.h b/tools/lib/sys_net.h
new file mode 100644 (file)
index 0000000..da6c1e8
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2001 - 2004 Mike Wray <mike.wray@hp.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef _XEN_LIB_SYS_NET_H_
+#define _XEN_LIB_SYS_NET_H_
+/** @file
+ *
+ * Replacement for standard network includes.
+ * Works in user or kernel code.
+ */
+
+extern int get_inet_addr(const char *s, unsigned long *address);
+extern unsigned long bits_to_mask(int n);
+extern int mask_to_bits(unsigned long mask);
+extern int get_host_address(const char *name, unsigned long *address);
+extern int get_service_port(const char *name, unsigned long *port);
+extern char *get_port_service(unsigned long port);
+extern int convert_service_to_port(const char *s, unsigned long *port);
+
+#ifdef __KERNEL__
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <asm/byteorder.h> 
+
+#ifndef htonl
+#define htonl(x) __constant_htonl(x)
+#endif
+
+#ifndef ntohl
+#define ntohl(x) __constant_ntohl(x)
+#endif
+
+#ifndef htons
+#define htons(x) __constant_htons(x)
+#endif
+
+#ifndef ntohs
+#define ntohs(x) __constant_ntohs(x)
+#endif
+
+#include <linux/in.h>
+extern char *inet_ntoa(struct in_addr inaddr);
+extern int inet_aton(const char *address, struct in_addr *inp);
+
+#else
+
+#include <limits.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+
+#endif
+
+extern char *mac_ntoa(const unsigned char *macaddr);
+extern int mac_aton(const char *addr, unsigned char *macaddr);
+
+#endif /* !_SP_SYS_NET_H_ */
+
+
+
diff --git a/tools/lib/sys_string.c b/tools/lib/sys_string.c
new file mode 100644 (file)
index 0000000..13a90df
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2001 - 2004 Mike Wray <mike.wray@hp.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifdef __KERNEL__
+#  include <linux/config.h>
+#  include <linux/module.h>
+#  include <linux/kernel.h>
+#  include <linux/errno.h>
+#else
+#  include <errno.h>
+#endif
+
+#include "allocate.h"
+#include "sys_string.h"
+
+/** Set the base to use for converting a string to a number.  Base is
+ * hex if starts with 0x, otherwise decimal.
+ *
+ * @param s input string
+ * @param base where to put the base
+ * @return rest of s to parse as a number
+ */
+inline static const char * convert_set_base(const char *s, int *base){
+    *base = 10;
+    if(s){
+        if(*s=='0'){
+            s++;
+            if(*s=='x' || *s=='X'){
+                *base = 16;
+                s++;
+            }
+        }
+    }
+    return s;
+}
+
+/** Get the numerical value of a digit in the given base.
+ *
+ * @param c digit character
+ * @param base to use
+ * @return numerical value of digit in range 0..base-1 or
+ * -1 if not in range for the base
+ */
+inline static int convert_get_digit(char c, int base){
+    int d;
+
+    if('0'<=c  && c<='9'){
+        d = c - '0';
+    } else if('a'<=c && c<='f'){
+        d = c - 'a' + 10;
+    } else if('A'<=c && c<='F'){
+        d = c - 'A' + 10;
+    } else {
+        d = -1;
+    }
+    return (d < base ? d : -1);
+}
+
+/** Convert a string to an unsigned long by parsing it as a number.
+ * Will accept hex or decimal in usual C syntax.
+ *
+ * @param str input string
+ * @param val where to put the result
+ * @return 0 if converted OK, negative otherwise
+ */
+int convert_atoul(const char *str, unsigned long *val){
+    int err = 0;
+    unsigned long v = 0;
+    int base;
+    const char *s = str;
+
+    if(!s) {
+        err = -EINVAL;
+        goto exit;
+    }
+    s = convert_set_base(s, &base);
+    for( ; !err && *s; s++){
+        int digit = convert_get_digit(*s, base);
+        if(digit<0){
+            err = -EINVAL;
+            goto exit;
+        }
+        v *= base;
+        v += digit;
+    } 
+  exit:
+    *val = (err ? 0 : v);
+    return err;
+}
+
+/** Combine a directory path with a relative path to produce
+ * a new path.
+ *
+ * @param s directory path
+ * @param t relative path
+ * @return new combined path s/t
+ */
+int path_concat(char *s, char *t, char **val){
+    int err = 0;
+    int sn, tn, vn;
+    char *v;
+    sn = strlen(s);
+    if(sn > 0 && s[sn-1] == '/'){
+        sn--;
+    }
+    tn = strlen(t);
+    if(tn > 0 && t[0] == '/'){
+        tn--;
+    }
+    vn = sn+tn+1;
+    v = (char*)allocate(vn+1);
+    if(!v){
+        err = -ENOMEM;
+        goto exit;
+    }
+    strncpy(v, s, sn);
+    v[sn] = '/';
+    strncpy(v+sn+1, t, tn);
+    v[vn] = '\0';
+  exit:
+    *val = (err ? NULL : v);
+    return err;    
+}
diff --git a/tools/lib/sys_string.h b/tools/lib/sys_string.h
new file mode 100644 (file)
index 0000000..f39935f
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2001 - 2004 Mike Wray <mike.wray@hp.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef _XEN_LIB_SYS_STRING_H_
+#define _XEN_LIB_SYS_STRING_H_
+/** @file
+ * Replacement for standard string includes.
+ * Works in user or kernel code.
+ */
+/*============================================================================*/
+#ifdef __KERNEL__
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <stdarg.h>
+#include "allocate.h"
+
+#if 0
+static inline int tolower(int c){
+    return (c>='A' && c<='Z' ? (c-'A')+'a' : c);
+}
+#endif
+
+static inline int isalpha(int c){
+    return (c>='A' && c<='Z') || (c>='a' && c<='z');
+}
+
+static inline int isdigit(int c){
+   return (c>='0' && c<='9');
+}
+
+#if 0
+static inline int strcasecmp(const char *s1, const char *s2){
+       int c1, c2;
+
+       do {
+               c1 = tolower(*s1++);
+               c2 = tolower(*s2++);
+       } while (c1 && c1 == c2);
+       return c1 - c2;
+}
+#endif
+
+static inline char * strdup(const char *s){
+    int n = (s ? 1+strlen(s) : 0);
+    char *copy = (n ? allocate(n) : NULL);
+    if(copy){
+        strcpy(copy, s);
+    }
+    return copy;
+}
+
+/*============================================================================*/
+#else
+#include <string.h>
+#include <stdio.h>
+
+#ifndef _GNU_SOURCE
+static inline size_t strnlen(const char *s, size_t n){
+    int k = 0;
+    if(s){
+       for(k=0; *s && k<n; s++, k++){}
+    }
+    return k;
+}
+#endif
+
+#endif
+/*============================================================================*/
+
+extern int convert_atoul(const char *s, unsigned long *v);
+extern int path_concat(char *s, char *t, char **val);
+
+#endif /* !_XEN_LIB_SYS_STRING_H_ */
diff --git a/tools/lib/xdr.c b/tools/lib/xdr.c
new file mode 100644 (file)
index 0000000..dfb91f2
--- /dev/null
@@ -0,0 +1,246 @@
+/* $Id: xdr.c,v 1.3 2003/09/29 13:40:00 mjw Exp $ */
+#include "xdr.h"
+#include <errno.h>
+/** @file
+ * XDR packer/unpacker for elements.
+ *
+ * string -> [T_STRING] [len:u32] <len bytes>
+ * atom   -> [T_ATOM]   [len:u32] <len bytes>
+ * uint   -> [T_UINT]   [value]
+ * cons   -> [T_CONS]   <car> <cdr>
+ * null   -> [T_NULL]
+ * none   -> [T_NONE]
+ * bool   -> [T_BOOL]   { 0:u8 | 1:u8 }
+ *
+ * types packed as u16.
+ *
+ * So (a b c) -> [T_CONS] a [T_CONS] b [T_CONS] c [T_NULL]
+ *    ()      -> [T_NULL]
+ */
+
+int pack_bool(IOStream *io, int x){
+    int err=0;
+    err = IOStream_print(io, "%c", 0xff & x);
+    if(err > 0) err = 0;
+    return err;
+}
+
+int unpack_bool(IOStream *io, int *x){
+    int err = 0;
+    int c;
+    c = IOStream_getc(io);
+    *x = (c < 0 ? 0 : c);
+    err = IOStream_error(io);
+    if(c < 0 && !err) err = -EIO;
+    return err;
+}
+
+int pack_ushort(IOStream *io, unsigned short x){
+    int err=0;
+    err = IOStream_print(io, "%c%c",
+                         0xff & (x >>  8),
+                         0xff & (x      ));
+    if(err > 0) err = 0;
+    return err;
+}
+
+int unpack_ushort(IOStream *io, unsigned short *x){
+    int err = 0;
+    int i, c = 0;
+    *x = 0;
+    for(i = 0; i< 2; i++){
+        c = IOStream_getc(io);
+        if(c < 0) break;
+        *x <<= 8;
+        *x |= (0xff & c);
+    }
+    err = IOStream_error(io);
+    if(c < 0 && !err) err = -EIO;
+    return err;
+}
+
+int pack_uint(IOStream *io, unsigned int x){
+    int err=0;
+    err = IOStream_print(io, "%c%c%c%c",
+                         0xff & (x >> 24),
+                         0xff & (x >> 16),
+                         0xff & (x >>  8),
+                         0xff & (x      ));
+    if(err > 0) err = 0;
+    return err;
+}
+
+int unpack_uint(IOStream *io, unsigned int *x){
+    int err = 0;
+    int i, c = 0;
+    *x = 0;
+    for(i = 0; i< 4; i++){
+        c = IOStream_getc(io);
+        if(c < 0) break;
+        *x <<= 8;
+        *x |= (0xff & c);
+    }
+    err = IOStream_error(io);
+    if(c < 0 && !err) err = -EIO;
+    return err;
+}
+
+int pack_string(IOStream *io, Sxpr x){
+    int err = 0;
+    int n = string_length(x);
+    char *s = string_string(x);
+    int i;
+    err = pack_uint(io, n);
+    if(err) goto exit;
+    for(i = 0; i < n; i++){
+        err = IOStream_print(io, "%c", s[i]);
+        if(err < 0) break;
+    }
+    if(err > 0) err = 0;
+  exit:
+    return err;
+}
+
+int unpack_string(IOStream *io, Sxpr *x){
+    int err;
+    unsigned int n;
+    int i, c = 0;
+    char *s;
+    Sxpr val = ONONE;
+    
+    err = unpack_uint(io, &n);
+    if(err) goto exit;
+    val = halloc(n+1, T_STRING);
+    if(NOMEMP(val)){
+        err = -ENOMEM;
+        goto exit;
+    }
+    s = string_string(val);
+    for(i=0; i<n; i++){
+        c = IOStream_getc(io);
+        if(c < 0) break;
+        s[i] = (char)c;
+    }
+    s[n] = '\0';
+  exit:
+    err = IOStream_error(io);
+    if(c < 0 && !err) err = -EIO;
+    if(err){
+        objfree(val);
+        val = ONONE;
+    }
+    *x = val;
+    return err;
+}
+
+int pack_cons(IOStream *io, Sxpr x){
+    int err = 0;
+    err = pack_sxpr(io, CAR(x));
+    if(err) goto exit;
+    err = pack_sxpr(io, CDR(x));
+  exit:
+    return err;
+}
+
+int unpack_cons(IOStream *io, Sxpr *x){
+    int err = 0;
+    Sxpr u = ONONE, v = ONONE, val = ONONE;
+    err = unpack_sxpr(io, &u);
+    if(err) goto exit;
+    err = unpack_sxpr(io, &v);
+    if(err) goto exit;
+    val = cons_new(u, v);
+    if(NOMEMP(val)){
+        err = -ENOMEM;
+    }
+  exit:
+    if(err){
+        objfree(u);
+        objfree(v);
+        val = ONONE;
+    }        
+    *x = val;
+    return err;
+}
+
+int pack_sxpr(IOStream *io, Sxpr x){
+    int err = 0;
+    unsigned short type = get_type(x);
+    err = pack_ushort(io, type);
+    if(err) goto exit;
+    switch(type){
+    case T_NULL:
+        break;
+    case T_NONE:
+        break;
+        break;
+    case T_BOOL:
+        err = pack_bool(io, get_ul(x));
+        break;
+    case T_CONS:
+        err = pack_cons(io, x);
+        break;
+    case T_ATOM:
+        err = pack_string(io, OBJ_ATOM(x)->name);
+        break;
+    case T_STRING:
+        err = pack_string(io, x);
+        break;
+    case T_UINT:
+        err = pack_uint(io, get_ul(x));
+        break;
+    default:
+        err = -EINVAL;
+        IOStream_print(iostderr, "%s> invalid type %d\n", __FUNCTION__, type);
+        break;
+    }
+  exit:
+    return err;
+}
+
+int unpack_sxpr(IOStream *io, Sxpr *x){
+    int err = 0;
+    unsigned short type;
+    unsigned int u;
+    Sxpr val = ONONE, y;
+
+    err = unpack_ushort(io, &type);
+    if(err) goto exit;
+    switch(type){
+    case T_NULL:
+        val = ONULL;
+        break;
+    case T_NONE:
+        val = ONONE;
+        break;
+    case T_CONS:
+        err = unpack_cons(io, &val);
+        break;
+    case T_BOOL:
+        err = unpack_bool(io, &u);
+        if(err) goto exit;
+        val = (u ? OTRUE : OFALSE);
+        break;
+    case T_ATOM:
+        err = unpack_string(io, &y);
+        if(err) goto exit;
+        val = intern(string_string(y));
+        objfree(y);
+        break;
+    case T_STRING:
+        err = unpack_string(io, &val);
+        break;
+    case T_UINT:
+        err = unpack_uint(io, &u);
+        if(err) goto exit;
+        val = OBJI(type, u);
+        break;
+    default:
+        err = -EINVAL;
+        IOStream_print(iostderr, "%s> invalid type %d\n", __FUNCTION__, type);
+        break;
+    }
+  exit:
+    *x = (err ? ONONE : val);
+    return err;
+}
diff --git a/tools/lib/xdr.h b/tools/lib/xdr.h
new file mode 100644 (file)
index 0000000..cb7d97d
--- /dev/null
@@ -0,0 +1,14 @@
+/* $Id: xdr.h,v 1.2 2003/09/29 13:40:00 mjw Exp $ */
+#ifndef _SP_XDR_H_
+#define _SP_XDR_H_
+#include "iostream.h"
+#include "sxpr.h"
+int pack_uint(IOStream *out, unsigned int x);
+int unpack_uint(IOStream *in, unsigned int *x);
+int pack_string(IOStream *out, Sxpr x);
+int unpack_string(IOStream *in, Sxpr *x);
+int pack_cons(IOStream *out, Sxpr x);
+int unpack_cons(IOStream *in, Sxpr *x);
+int pack_sxpr(IOStream *out, Sxpr x);
+int unpack_sxpr(IOStream *in, Sxpr *x);
+#endif /* _SP_XDR_H_ */
index 271a9c470372c51d534e79e0930e7f47ed046aa6..a94a082004afecd3b428c9e6df75053eae9c3f2b 100644 (file)
@@ -4,13 +4,71 @@ MINOR    = 0
 SONAME   = libxc.so.$(MAJOR)
 
 CC       = gcc
-CFLAGS   = -c -Werror -O3 -fno-strict-aliasing
-CFLAGS  += -I../../../xen/include/hypervisor-ifs
-CFLAGS  += -I../../xu/lib
-CFLAGS  += -I../../../linux-xen-sparse/include
 
-HDRS     = $(wildcard *.h)
-OBJS     = $(patsubst %.c,%.o,$(wildcard *.c))
+XEN_ROOT = ../../..
+
+vpath %.h      $(XEN_ROOT)/xen/include/hypervisor-ifs
+INCLUDES += -I $(XEN_ROOT)/xen/include/hypervisor-ifs
+
+vpath %.h      $(XEN_ROOT)/tools/xu/lib
+INCLUDES += -I $(XEN_ROOT)/tools/xu/lib
+
+vpath %h       $(XEN_ROOT)/linux-xen-sparse/include
+INCLUDES += -I $(XEN_ROOT)/linux-xen-sparse/include
+
+vpath %c       $(XEN_ROOT)/tools/lib
+INCLUDES += -I $(XEN_ROOT)/tools/lib
+
+LIB_SRCS :=
+LIB_SRCS += allocate.c
+#LIB_SRCS += enum.c
+LIB_SRCS += file_stream.c
+LIB_SRCS += gzip_stream.c
+#LIB_SRCS += hash_table.c
+LIB_SRCS += iostream.c
+#LIB_SRCS += kernel_stream.c
+#LIB_SRCS += lexis.c
+#LIB_SRCS += lzi_stream.c
+#LIB_SRCS += lzo_stream.c
+#LIB_SRCS += marshal.c
+#LIB_SRCS += socket_stream.c
+#LIB_SRCS += string_stream.c
+#LIB_SRCS += sxpr.c
+#LIB_SRCS += sxpr_parser.c
+LIB_SRCS += sys_net.c
+LIB_SRCS += sys_string.c
+#LIB_SRCS += xdr.c
+
+SRCS     :=
+SRCS     += xc_atropos.c
+SRCS     += xc_bvtsched.c
+SRCS     += xc_domain.c
+SRCS     += xc_evtchn.c
+SRCS     += xc_io.c
+SRCS     += xc_linux_build.c
+SRCS     += xc_linux_restore.c
+SRCS     += xc_linux_save.c
+SRCS     += xc_misc.c
+SRCS     += xc_netbsd_build.c
+SRCS     += xc_physdev.c
+SRCS     += xc_private.c
+SRCS     += $(LIB_SRCS)
+
+#CFLAGS  += -I../../../xen/include/hypervisor-ifs
+#CFLAGS  += -I../../xu/lib
+#CFLAGS  += -I../../../linux-xen-sparse/include
+
+CFLAGS   += -Wall
+CFLAGS   += -Werror
+CFLAGS   += -g
+CFLAGS   += -O3
+CFLAGS   += -fno-strict-aliasing
+CFLAGS   += $(INCLUDES)
+# Get gcc to generate the dependencies for us.
+CFLAGS   += -Wp,-MD,.$(@F).d
+DEPS     = .*.d
+
+OBJS     = $(patsubst %.c,%.o,$(SRCS))
 
 LIB      = libxc.so libxc.so.$(MAJOR) libxc.so.$(MAJOR).$(MINOR)
 
@@ -32,6 +90,8 @@ install: all
 
 clean:
        $(RM) *.a *.so *.o *.rpm $(LIB)
+       $(RM) *~
+       $(RM) $(DEPS)
 
 rpm: all
        rm -rf staging
@@ -49,5 +109,8 @@ libxc.so.$(MAJOR):
 libxc.so.$(MAJOR).$(MINOR): $(OBJS)
        $(CC) -Wl,-soname -Wl,$(SONAME) -shared -o $@ $^ -lz
 
-%.o: %.c $(HDRS) Makefile
-       $(CC) $(CFLAGS) -o $@ $<
+%.o: %.c Makefile
+
+#      $(CC) $(CFLAGS) -o $@ $<
+
+-include $(DEPS)
index 274f2fddede57286bbbae611117573196e3b96d1..adef112dbd4e0308495832c3155a6513f08784bf 100644 (file)
@@ -68,18 +68,10 @@ int xc_shadow_control(int xc_handle,
 #define XCFLAGS_LIVE    2
 #define XCFLAGS_DEBUG   4
 
-int xc_linux_save(int xc_handle,
-                  u32 domid, 
-                  unsigned int flags,
-                 int (*writerfn)(void *, const void *, size_t),
-                 void *writerst );
+struct XcIOContext;
+int xc_linux_save(int xc_handle, struct XcIOContext *ioctxt);
 
-int xc_linux_restore(int xc_handle,
-                     u32 domid,
-                     unsigned int flags,                    
-                    int (*readerfn)(void *, void *, size_t),
-                    void *readerst,
-                     u32 *pdomid);
+int xc_linux_restore(int xc_handle, struct XcIOContext *ioctxt);
 
 int xc_linux_build(int xc_handle,
                    u32 domid,
diff --git a/tools/xc/lib/xc_io.c b/tools/xc/lib/xc_io.c
new file mode 100644 (file)
index 0000000..7d75ea2
--- /dev/null
@@ -0,0 +1,27 @@
+#include "xc_io.h"
+
+void xcio_error(XcIOContext *ctxt, const char *msg, ...){
+  va_list args;
+
+  va_start(args, msg);
+  IOStream_vprint(ctxt->info, msg, args);
+  va_end(args);
+}
+
+void xcio_info(XcIOContext *ctxt, const char *msg, ...){
+  va_list args;
+
+  if(!(ctxt->flags & XCFLAGS_VERBOSE)) return;
+  va_start(args, msg);
+  IOStream_vprint(ctxt->info, msg, args);
+  va_end(args);
+}
+
+void xcio_debug(XcIOContext *ctxt, const char *msg, ...){
+  va_list args;
+
+  if(!(ctxt->flags & XCFLAGS_DEBUG)) return;
+  va_start(args, msg);
+  IOStream_vprint(ctxt->info, msg, args);
+  va_end(args);
+}
diff --git a/tools/xc/lib/xc_io.h b/tools/xc/lib/xc_io.h
new file mode 100644 (file)
index 0000000..7a23cb9
--- /dev/null
@@ -0,0 +1,44 @@
+#ifndef __XC_XC_IO_H__
+#define __XC_XC_IO_H__
+
+#include "xc_private.h"
+#include <iostream.h>
+
+typedef struct XcIOContext {
+    u32 domain;
+    unsigned flags;
+    IOStream *io;
+    IOStream *info;
+    IOStream *err;
+    char *vmconfig;
+    int vmconfig_n;
+} XcIOContext;
+
+static inline int xcio_read(XcIOContext *ctxt, void *buf, int n){
+    int rc;
+
+    rc = IOStream_read(ctxt->io, buf, n);
+    return (rc == n ? 0 : rc);
+}
+
+static inline int xcio_write(XcIOContext *ctxt, void *buf, int n){
+    int rc;
+
+    rc = IOStream_write(ctxt->io, buf, n);
+    return (rc == n ? 0 : rc);
+}
+
+static inline int xcio_flush(XcIOContext *ctxt){
+    return IOStream_flush(ctxt->io);
+}
+
+extern void xcio_error(XcIOContext *ctxt, const char *msg, ...);
+extern void xcio_info(XcIOContext *ctxt, const char *msg, ...);
+
+#define xcio_perror(_ctxt, _msg...) \
+xcio_error(_ctxt, "(errno %d %s)" _msg, errno, strerror(errno), ## _msg)
+
+#endif /* ! __XC_XC_IO_H__ */
+
+
+
index 7387cddcf0214cfae5b6e6e946a24c56cd9e366b..badba75162e5d808ab56b69e291bb436bf439c76 100644 (file)
@@ -8,7 +8,6 @@
 
 #include "xc_private.h"
 #include <asm-xen/suspend.h>
-#include <zlib.h>
 
 #define MAX_BATCH_SIZE 1024
 
 #endif
 
 
-/* This may allow us to create a 'quiet' command-line option, if necessary. */
-#define verbose_printf(_f, _a...) \
-    do {                          \
-        if ( !verbose ) break;    \
-        printf( _f , ## _a );     \
-        fflush(stdout);           \
-    } while ( 0 )
-
 static int get_pfn_list(int xc_handle,
                         u32 domain_id, 
                         unsigned long *pfn_buf, 
@@ -54,19 +45,44 @@ static int get_pfn_list(int xc_handle,
     return (ret < 0) ? -1 : op.u.getmemlist.num_pfns;
 }
 
+/** Read the vmconfig string from the state input.
+ * It is stored as a 4-byte count 'n' followed by n bytes.
+ * The config data is stored in a new string in 'ioctxt->vmconfig',
+ * and is null-terminated. The count is stored in 'ioctxt->vmconfig_n'.
+ *
+ * @param ioctxt i/o context
+ * @return 0 on success, non-zero on error.
+ */
+static int read_vmconfig(XcIOContext *ioctxt){
+    int err = -1;
+    if(xcio_read(ioctxt, &ioctxt->vmconfig_n, sizeof(ioctxt->vmconfig_n))){
+        goto exit;
+    }
+    ioctxt->vmconfig = malloc(ioctxt->vmconfig_n + 1);
+    if(!ioctxt->vmconfig) goto exit;
+    if(xcio_read(ioctxt, ioctxt->vmconfig, ioctxt->vmconfig_n)){
+        goto exit;
+    }
+    ioctxt->vmconfig[ioctxt->vmconfig_n] = '\0';
+    err = 0;
+  exit:
+    if(err){
+        if(ioctxt->vmconfig){
+            free(ioctxt->vmconfig);
+        }
+        ioctxt->vmconfig = NULL;
+        ioctxt->vmconfig_n = 0;
+    }
+    return err;
+}
 
-int xc_linux_restore(int xc_handle,
-                     u32 dom,
-                     unsigned int flags,
-                     int (*readerfn)(void *, void *, size_t),
-                     void *readerst,
-                     u32 *pdomid)
+int xc_linux_restore(int xc_handle, XcIOContext *ioctxt)
 {
     dom0_op_t op;
-    int rc = 1, i, j, n, k;
+    int rc = 1, i, n, k;
     unsigned long mfn, pfn, xpfn;
     unsigned int prev_pc, this_pc;
-    int verbose = flags & XCFLAGS_VERBOSE;
+    u32 dom = ioctxt->domain;
     int verify = 0; 
 
     /* Number of page frames in use by this Linux session. */
@@ -115,8 +131,7 @@ int xc_linux_restore(int xc_handle,
     /* used by debug verify code */
     unsigned long buf[PAGE_SIZE/sizeof(unsigned long)];
 
-    if ( mlock(&ctxt, sizeof(ctxt) ) )
-    {   
+    if ( mlock(&ctxt, sizeof(ctxt) ) ) {   
         /* needed for when we do the build dom0 op, 
            but might as well do early */
         PERROR("Unable to mlock ctxt");
@@ -124,35 +139,36 @@ int xc_linux_restore(int xc_handle,
     }
 
     /* Start writing out the saved-domain record. */
-    if ( (*readerfn)(readerst, signature, 16) ||
-         (memcmp(signature, "LinuxGuestRecord", 16) != 0) )
-    {
-        ERROR("Unrecognised state format -- no signature found");
+    if ( xcio_read(ioctxt, signature, 16) ||
+         (memcmp(signature, "LinuxGuestRecord", 16) != 0) ) {
+        xcio_error(ioctxt, "Unrecognised state format -- no signature found");
         goto out;
     }
 
-    if ( (*readerfn)(readerst, name,                  sizeof(name)) ||
-         (*readerfn)(readerst, &nr_pfns,              sizeof(unsigned long)) ||
-         (*readerfn)(readerst, pfn_to_mfn_frame_list, PAGE_SIZE) )
-    {
-        ERROR("Error when reading from state file");
+    if ( xcio_read(ioctxt, name,                  sizeof(name)) ||
+         xcio_read(ioctxt, &nr_pfns,              sizeof(unsigned long)) ||
+         xcio_read(ioctxt, pfn_to_mfn_frame_list, PAGE_SIZE) ) {
+        xcio_error(ioctxt, "Error reading header");
         goto out;
     }
 
-    for ( i = 0; i < MAX_DOMAIN_NAME; i++ )
-    {
+    if(read_vmconfig(ioctxt)){
+        xcio_error(ioctxt, "Error writing vmconfig");
+        goto out;
+    }
+
+    for ( i = 0; i < MAX_DOMAIN_NAME; i++ ) {
         if ( name[i] == '\0' ) break;
         if ( name[i] & 0x80 )
         {
-            ERROR("Random characters in domain name");
+            xcio_error(ioctxt, "Random characters in domain name");
             goto out;
         }
     }
     name[MAX_DOMAIN_NAME-1] = '\0';
 
-    if ( nr_pfns > 1024*1024 )
-    {
-        ERROR("Invalid state file -- pfn count out of range");
+    if ( nr_pfns > 1024*1024 ) {
+        xcio_error(ioctxt, "Invalid state file -- pfn count out of range");
         goto out;
     }
 
@@ -162,22 +178,19 @@ int xc_linux_restore(int xc_handle,
     region_mfn       = calloc(1, 4 * MAX_BATCH_SIZE);    
 
     if ( (pfn_to_mfn_table == NULL) || (pfn_type == NULL) || 
-         (region_mfn == NULL) )
-    {
+         (region_mfn == NULL) ) {
         errno = ENOMEM;
         goto out;
     }
     
-    if ( mlock(region_mfn, 4 * MAX_BATCH_SIZE ) )
-    {
-        ERROR("Could not mlock region_mfn");
+    if ( mlock(region_mfn, 4 * MAX_BATCH_SIZE ) ) {
+        xcio_error(ioctxt, "Could not mlock region_mfn");
         goto out;
     }
 
     /* Set the domain's name to that from the restore file */
-    if ( xc_domain_setname( xc_handle, dom, name ) )
-    {
-        ERROR("Could not set domain name");
+    if ( xc_domain_setname( xc_handle, dom, name ) ) {
+        xcio_error(ioctxt, "Could not set domain name");
         goto out;
     }
 
@@ -187,7 +200,7 @@ int xc_linux_restore(int xc_handle,
     if ( xc_domain_setinitialmem(xc_handle, dom, 
                                  nr_pfns * (PAGE_SIZE / 1024)) )
     {
-        ERROR("Could not set domain initial memory");
+        xcio_error(ioctxt, "Could not set domain initial memory");
         goto out;
     }
 
@@ -195,9 +208,8 @@ int xc_linux_restore(int xc_handle,
     op.cmd = DOM0_GETDOMAININFO;
     op.u.getdomaininfo.domain = (domid_t)dom;
     op.u.getdomaininfo.ctxt = NULL;
-    if ( do_dom0_op(xc_handle, &op) < 0 )
-    {
-        ERROR("Could not get information on new domain");
+    if ( do_dom0_op(xc_handle, &op) < 0 ) {
+        xcio_error(ioctxt, "Could not get information on new domain");
         goto out;
     }
     shared_info_frame = op.u.getdomaininfo.shared_info_frame;
@@ -208,19 +220,17 @@ int xc_linux_restore(int xc_handle,
 
 
     /* Build the pfn-to-mfn table. We choose MFN ordering returned by Xen. */
-    if ( get_pfn_list(xc_handle, dom, pfn_to_mfn_table, nr_pfns) != nr_pfns )
-    {
-        ERROR("Did not read correct number of frame numbers for new dom");
+    if ( get_pfn_list(xc_handle, dom, pfn_to_mfn_table, nr_pfns) != nr_pfns ) {
+        xcio_error(ioctxt, "Did not read correct number of frame numbers for new dom");
         goto out;
     }
 
-    if ( (mmu = init_mmu_updates(xc_handle, dom)) == NULL )
-    {
-        ERROR("Could not initialise for MMU updates");
+    if ( (mmu = init_mmu_updates(xc_handle, dom)) == NULL ) {
+        xcio_error(ioctxt, "Could not initialise for MMU updates");
         goto out;
     }
 
-    verbose_printf("Reloading memory pages:   0%%");
+    xcio_info(ioctxt, "Reloading memory pages:   0%%");
 
     /*
      * Now simply read each saved frame into its new machine frame.
@@ -229,56 +239,45 @@ int xc_linux_restore(int xc_handle,
     prev_pc = 0;
 
     n=0;
-    while(1)
-    {
+    while(1) {
         int j;
         unsigned long region_pfn_type[MAX_BATCH_SIZE];
 
         this_pc = (n * 100) / nr_pfns;
-        if ( (this_pc - prev_pc) >= 5 )
-        {
-            verbose_printf("\b\b\b\b%3d%%", this_pc);
+        if ( (this_pc - prev_pc) >= 5 ) {
+            xcio_info(ioctxt, "\b\b\b\b%3d%%", this_pc);
             prev_pc = this_pc;
         }
 
-        if ( (*readerfn)(readerst, &j, sizeof(int)) )
-        {
-            ERROR("Error when reading from state file");
+        if ( xcio_read(ioctxt, &j, sizeof(int)) ) {
+            xcio_error(ioctxt, "Error when reading from state file");
             goto out;
         }
 
         DPRINTF("batch %d\n",j);
  
-        if ( j == -1 )
-        {
+        if ( j == -1 ) {
             verify = 1;
             printf("Entering page verify mode\n");
             continue;
         }
 
-        if ( j == 0 ) 
-            break;  /* our work here is done */
+        if ( j == 0 ) break;  /* our work here is done */
 
-        if( j > MAX_BATCH_SIZE )
-        {
-            ERROR("Max batch size exceeded. Giving up.");
+        if( j > MAX_BATCH_SIZE ) {
+            xcio_error(ioctxt, "Max batch size exceeded. Giving up.");
             goto out;
         }
  
-        if ( (*readerfn)(readerst, region_pfn_type, j*sizeof(unsigned long)) )
-        {
-            ERROR("Error when reading from state file");
+        if ( xcio_read(ioctxt, region_pfn_type, j*sizeof(unsigned long)) ) {
+            xcio_error(ioctxt, "Error when reading from state file");
             goto out;
         }
 
-        for(i=0;i<j;i++)
-        {
-            if ( (region_pfn_type[i] & LTAB_MASK) == XTAB)
-            {
+        for(i=0; i<j; i++) {
+            if ( (region_pfn_type[i] & LTAB_MASK) == XTAB) {
                 region_mfn[i] = 0; /* we know map will fail, but don't care */
-            }
-            else
-            {  
+            } else {  
                 pfn = region_pfn_type[i] & ~LTAB_MASK;
                 region_mfn[i] = pfn_to_mfn_table[pfn];
             }          
@@ -287,24 +286,20 @@ int xc_linux_restore(int xc_handle,
         if ( (region_base = mfn_mapper_map_batch( xc_handle, dom, 
                                                   PROT_WRITE,
                                                   region_mfn,
-                                                  j )) == 0)
-        {
-            PERROR("map batch failed");
+                                                  j )) == 0) {
+            xcio_error(ioctxt, "map batch failed");
             goto out;
         }
 
-        for(i=0;i<j;i++)
-        {
+        for(i=0;i<j;i++) {
             unsigned long *ppage;
 
             pfn = region_pfn_type[i] & ~LTAB_MASK;
 
-            if ( (region_pfn_type[i] & LTAB_MASK) == XTAB)
-                continue;
+            if ( (region_pfn_type[i] & LTAB_MASK) == XTAB) continue;
 
-            if (pfn>nr_pfns)
-            {
-                ERROR("pfn out of range");
+            if (pfn>nr_pfns) {
+                xcio_error(ioctxt, "pfn out of range");
                 goto out;
             }
 
@@ -314,36 +309,32 @@ int xc_linux_restore(int xc_handle,
 
             mfn = pfn_to_mfn_table[pfn];
 
-            if ( verify )
+            if ( verify ) {
                 ppage = (unsigned long*) buf;  /* debug case */
-            else
+            } else {
                 ppage = (unsigned long*) (region_base + i*PAGE_SIZE);
+            }
 
-            if ( (*readerfn)(readerst, ppage, PAGE_SIZE) )
-            {
-                ERROR("Error when reading from state file");
+            if ( xcio_read(ioctxt, ppage, PAGE_SIZE) ) {
+                xcio_error(ioctxt, "Error when reading from state file");
                 goto out;
             }
 
-            switch( region_pfn_type[i] )
-            {
+            switch( region_pfn_type[i] ) {
             case 0:
                 break;
 
             case L1TAB:
             {
-                for ( k = 0; k < 1024; k++ )
-                {
-                    if ( ppage[k] & _PAGE_PRESENT )
-                    {
+                for ( k = 0; k < 1024; k++ ) {
+                    if ( ppage[k] & _PAGE_PRESENT ) {
                         xpfn = ppage[k] >> PAGE_SHIFT;
 
-                        if ( xpfn >= nr_pfns )
-                        {
-                            ERROR("Frame number in type %d page table is "
-                                  "out of range. i=%d k=%d pfn=0x%x "
-                                  "nr_pfns=%d", region_pfn_type[i]>>28, i, 
-                                  k, xpfn,nr_pfns);
+                        if ( xpfn >= nr_pfns ) {
+                            xcio_error(ioctxt, "Frame number in type %lu page table is "
+                                  "out of range. i=%d k=%d pfn=0x%lx "
+                                  "nr_pfns=%lu", region_pfn_type[i]>>28, i, 
+                                  k, xpfn, nr_pfns);
                             goto out;
                         }
 
@@ -359,16 +350,13 @@ int xc_linux_restore(int xc_handle,
             {
                 for ( k = 0; 
                       k < (HYPERVISOR_VIRT_START>>L2_PAGETABLE_SHIFT); 
-                      k++ )
-                {
-                    if ( ppage[k] & _PAGE_PRESENT )
-                    {
+                      k++ ) {
+                    if ( ppage[k] & _PAGE_PRESENT ) {
                         xpfn = ppage[k] >> PAGE_SHIFT;
 
-                        if ( xpfn >= nr_pfns )
-                        {
-                            ERROR("Frame number in type %d page table is "
-                                  "out of range. i=%d k=%d pfn=%d nr_pfns=%d",
+                        if ( xpfn >= nr_pfns ) {
+                            xcio_error(ioctxt, "Frame number in type %lu page table is "
+                                  "out of range. i=%d k=%d pfn=%lu nr_pfns=%lu",
                                   region_pfn_type[i]>>28, i, k, xpfn, nr_pfns);
 
                             goto out;
@@ -383,24 +371,21 @@ int xc_linux_restore(int xc_handle,
             break;
 
             default:
-                ERROR("Bogus page type %x page table is out of range."
-                      " i=%d nr_pfns=%d", region_pfn_type[i], i, nr_pfns);
+                xcio_error(ioctxt, "Bogus page type %lx page table is out of range."
+                      " i=%d nr_pfns=%lu", region_pfn_type[i], i, nr_pfns);
                 goto out;
 
             } /* end of page type switch statement */
 
-            if ( verify )
-            {
+            if ( verify ) {
                 int res = memcmp(buf, (region_base + i*PAGE_SIZE), PAGE_SIZE );
-                if (res)
-                {
+                if (res) {
                     int v;
-                    printf("************** pfn=%x type=%x gotcs=%08lx "
+                    printf("************** pfn=%lx type=%lx gotcs=%08lx "
                            "actualcs=%08lx\n", pfn, pfn_type[pfn], 
                            csum_page(region_base + i*PAGE_SIZE), 
                            csum_page(buf));
-                    for ( v = 0; v < 4; v++ )
-                    {
+                    for ( v = 0; v < 4; v++ ) {
                         unsigned long *p = (unsigned long *)
                             (region_base + i*PAGE_SIZE);
                         if ( buf[v] != p[v] )
@@ -411,8 +396,7 @@ int xc_linux_restore(int xc_handle,
             }
 
             if ( add_mmu_update(xc_handle, mmu,
-                                (mfn<<PAGE_SHIFT) | MMU_MACHPHYS_UPDATE, pfn) )
-            {
+                                (mfn<<PAGE_SHIFT) | MMU_MACHPHYS_UPDATE, pfn) ) {
                 printf("machpys mfn=%ld pfn=%ld\n",mfn,pfn);
                 goto out;
             }
@@ -431,44 +415,36 @@ int xc_linux_restore(int xc_handle,
      * Pin page tables. Do this after writing to them as otherwise Xen
      * will barf when doing the type-checking.
      */
-    for ( i = 0; i < nr_pfns; i++ )
-    {
-        if ( pfn_type[i] == L1TAB )
-        {
+    for ( i = 0; i < nr_pfns; i++ ) {
+        if ( pfn_type[i] == L1TAB ) {
             if ( add_mmu_update(xc_handle, mmu,
                                 (pfn_to_mfn_table[i]<<PAGE_SHIFT) | 
                                 MMU_EXTENDED_COMMAND,
-                                MMUEXT_PIN_L1_TABLE) )
-            {
+                                MMUEXT_PIN_L1_TABLE) ) {
                 printf("ERR pin L1 pfn=%lx mfn=%lx\n",
-                       i, pfn_to_mfn_table[i]);
+                       (unsigned long)i, pfn_to_mfn_table[i]);
                 goto out;
             }
-        }
-        else if ( pfn_type[i] == L2TAB )
-        {
+        } else if ( pfn_type[i] == L2TAB ) {
             if ( add_mmu_update(xc_handle, mmu,
                                 (pfn_to_mfn_table[i]<<PAGE_SHIFT) | 
                                 MMU_EXTENDED_COMMAND,
-                                MMUEXT_PIN_L2_TABLE) )
-            {
+                                MMUEXT_PIN_L2_TABLE) ) {
                 printf("ERR pin L2 pfn=%lx mfn=%lx\n",
-                       i, pfn_to_mfn_table[i]);
+                       (unsigned long)i, pfn_to_mfn_table[i]);
                 goto out;
             }
         }
     }
 
-    if ( finish_mmu_updates(xc_handle, mmu) )
-        goto out;
+    if ( finish_mmu_updates(xc_handle, mmu) ) goto out;
 
-    verbose_printf("\b\b\b\b100%%\nMemory reloaded.\n");
+    xcio_info(ioctxt, "\b\b\b\b100%%\nMemory reloaded.\n");
 
 
-    if ( (*readerfn)(readerst, &ctxt,                 sizeof(ctxt)) ||
-         (*readerfn)(readerst, shared_info,           PAGE_SIZE) )
-    {
-        ERROR("Error when reading from state file");
+    if ( xcio_read(ioctxt, &ctxt,                 sizeof(ctxt)) ||
+         xcio_read(ioctxt, shared_info,           PAGE_SIZE) ) {
+        xcio_error(ioctxt, "Error when reading from state file");
         goto out;
     }
 
@@ -476,7 +452,7 @@ int xc_linux_restore(int xc_handle,
     pfn = ctxt.cpu_ctxt.esi;
     if ( (pfn >= nr_pfns) || (pfn_type[pfn] != NOTAB) )
     {
-        ERROR("Suspend record frame number is bad");
+        xcio_error(ioctxt, "Suspend record frame number is bad");
         goto out;
     }
     ctxt.cpu_ctxt.esi = mfn = pfn_to_mfn_table[pfn];
@@ -487,17 +463,14 @@ int xc_linux_restore(int xc_handle,
     unmap_pfn(pm_handle, p_srec);
 
     /* Uncanonicalise each GDT frame number. */
-    if ( ctxt.gdt_ents > 8192 )
-    {
-        ERROR("GDT entry count out of range");
+    if ( ctxt.gdt_ents > 8192 ) {
+        xcio_error(ioctxt, "GDT entry count out of range");
         goto out;
     }
-    for ( i = 0; i < ctxt.gdt_ents; i += 512 )
-    {
+    for ( i = 0; i < ctxt.gdt_ents; i += 512 ) {
         pfn = ctxt.gdt_frames[i];
-        if ( (pfn >= nr_pfns) || (pfn_type[pfn] != NOTAB) )
-        {
-            ERROR("GDT frame number is bad");
+        if ( (pfn >= nr_pfns) || (pfn_type[pfn] != NOTAB) ) {
+            xcio_error(ioctxt, "GDT frame number is bad");
             goto out;
         }
         ctxt.gdt_frames[i] = pfn_to_mfn_table[pfn];
@@ -505,11 +478,10 @@ int xc_linux_restore(int xc_handle,
 
     /* Uncanonicalise the page table base pointer. */
     pfn = ctxt.pt_base >> PAGE_SHIFT;
-    if ( (pfn >= nr_pfns) || (pfn_type[pfn] != L2TAB) )
-    {
-        printf("PT base is bad. pfn=%d nr=%d type=%08lx %08lx\n",
-               pfn, nr_pfns, pfn_type[pfn], L2TAB);
-        ERROR("PT base is bad.");
+    if ( (pfn >= nr_pfns) || (pfn_type[pfn] != L2TAB) ) {
+        printf("PT base is bad. pfn=%lu nr=%lu type=%08lx %08lx\n",
+               pfn, nr_pfns, pfn_type[pfn], (unsigned long)L2TAB);
+        xcio_error(ioctxt, "PT base is bad.");
         goto out;
     }
     ctxt.pt_base = pfn_to_mfn_table[pfn] << PAGE_SHIFT;
@@ -527,14 +499,12 @@ int xc_linux_restore(int xc_handle,
 
 
     /* Uncanonicalise the pfn-to-mfn table frame-number list. */
-    for ( i = 0; i < (nr_pfns+1023)/1024; i++ )
-    {
+    for ( i = 0; i < (nr_pfns+1023)/1024; i++ ) {
         unsigned long pfn, mfn;
 
         pfn = pfn_to_mfn_frame_list[i];
-        if ( (pfn >= nr_pfns) || (pfn_type[pfn] != NOTAB) )
-        {
-            ERROR("PFN-to-MFN frame number is bad");
+        if ( (pfn >= nr_pfns) || (pfn_type[pfn] != NOTAB) ) {
+            xcio_error(ioctxt, "PFN-to-MFN frame number is bad");
             goto out;
         }
         mfn = pfn_to_mfn_table[pfn];
@@ -545,9 +515,8 @@ int xc_linux_restore(int xc_handle,
           mfn_mapper_map_batch(xc_handle, dom, 
                                PROT_WRITE,
                                pfn_to_mfn_frame_list,
-                               (nr_pfns+1023)/1024 )) == 0 )
-    {
-        ERROR("Couldn't map pfn_to_mfn table");
+                               (nr_pfns+1023)/1024 )) == 0 ) {
+        xcio_error(ioctxt, "Couldn't map pfn_to_mfn table");
         goto out;
     }
 
@@ -569,24 +538,26 @@ int xc_linux_restore(int xc_handle,
      *  9. debugregs are checked by Xen.
      *  10. callback code selectors need checking.
      */
-    for ( i = 0; i < 256; i++ )
-    {
+    for ( i = 0; i < 256; i++ ) {
         ctxt.trap_ctxt[i].vector = i;
         if ( (ctxt.trap_ctxt[i].cs & 3) == 0 )
             ctxt.trap_ctxt[i].cs = FLAT_GUESTOS_CS;
     }
-    if ( (ctxt.guestos_ss & 3) == 0 )
+    if ( (ctxt.guestos_ss & 3) == 0 ){
         ctxt.guestos_ss = FLAT_GUESTOS_DS;
-    if ( (ctxt.event_callback_cs & 3) == 0 )
+    }
+    if ( (ctxt.event_callback_cs & 3) == 0 ){
         ctxt.event_callback_cs = FLAT_GUESTOS_CS;
-    if ( (ctxt.failsafe_callback_cs & 3) == 0 )
+    }
+    if ( (ctxt.failsafe_callback_cs & 3) == 0 ){
         ctxt.failsafe_callback_cs = FLAT_GUESTOS_CS;
+    }
     if ( ((ctxt.ldt_base & (PAGE_SIZE - 1)) != 0) ||
          (ctxt.ldt_ents > 8192) ||
          (ctxt.ldt_base > HYPERVISOR_VIRT_START) ||
          ((ctxt.ldt_base + ctxt.ldt_ents*8) > HYPERVISOR_VIRT_START) )
     {
-        ERROR("Bad LDT base or size");
+        xcio_error(ioctxt, "Bad LDT base or size");
         goto out;
     }
    
@@ -597,34 +568,33 @@ int xc_linux_restore(int xc_handle,
 
     /* don't start the domain as we have console etc to set up */
   
-    if( rc == 0 )
-    {
+    if( rc == 0 ) {
         /* Success: print the domain id. */
-        verbose_printf("DOM=%u\n", dom);
+        xcio_info(ioctxt, "DOM=%lu\n", dom);
         return 0;
     }
 
 
  out:
-    if ( (rc != 0) && (dom != 0) )
+    if ( (rc != 0) && (dom != 0) ){
         xc_domain_destroy(xc_handle, dom);
-
-    if ( mmu != NULL )
+    }
+    if ( mmu != NULL ){
         free(mmu);
-
-    if ( pm_handle >= 0 )
+    }
+    if ( pm_handle >= 0 ){
         (void)close_pfn_mapper(pm_handle);
-
-    if ( pfn_to_mfn_table != NULL )
+    }
+    if ( pfn_to_mfn_table != NULL ){
         free(pfn_to_mfn_table);
-    if ( pfn_type != NULL )
+    }
+    if ( pfn_type != NULL ){
         free(pfn_type);
+    }
 
-
-    if ( rc == 0 )
-        *pdomid = dom;
-
+    if ( rc == 0 ){
+        ioctxt->domain = dom;
+    }
     DPRINTF("Restore exit with rc=%d\n",rc);
-
     return rc;
 }
index 2d035ff7ea973d8e816d09048a9fddf8e296c2e3..6eea1b4276a7212685a5f8d122ec71c63ac537b4 100644 (file)
@@ -6,6 +6,7 @@
  * Copyright (c) 2003, K A Fraser.
  */
 
+#include <sys/time.h>
 #include "xc_private.h"
 #include <asm-xen/suspend.h>
 
 #define DDPRINTF(_f, _a...) ((void)0)
 #endif
 
-
-
-/* This may allow us to create a 'quiet' command-line option, if necessary. */
-#define verbose_printf(_f, _a...) \
-    do {                          \
-        if ( !verbose ) break;    \
-        printf( _f , ## _a );     \
-        fflush(stdout);           \
-    } while ( 0 )
-
 /*
  * Returns TRUE if the given machine frame number has a unique mapping
  * in the guest's pseudophysical map.
@@ -197,20 +188,30 @@ static int track_cpu_usage( int xc_handle, u32 domid, int faults,
     return 0;
 }
 
+/** Write the vmconfig string.
+ * It is stored as a 4-byte count 'n' followed by n bytes.
+ *
+ * @param ioctxt i/o context
+ * @return 0 on success, non-zero on error.
+ */
+static int write_vmconfig(XcIOContext *ioctxt){
+    int err = -1;
+    if(xcio_write(ioctxt, &ioctxt->vmconfig_n, sizeof(ioctxt->vmconfig_n))) goto exit;
+    if(xcio_write(ioctxt, ioctxt->vmconfig, ioctxt->vmconfig_n)) goto exit;
+    err = 0;
+  exit:
+    return err;
+}
 
-int xc_linux_save(int xc_handle,
-                  u32 domid, 
-                  unsigned int flags,
-                  int (*writerfn)(void *, const void *, size_t),
-                  void *writerst )
+int xc_linux_save(int xc_handle, XcIOContext *ioctxt)
 {
     dom0_op_t op;
     int rc = 1, i, j, k, last_iter, iter = 0;
     unsigned long mfn;
-    int verbose = flags & XCFLAGS_VERBOSE;
-    int live = flags & XCFLAGS_LIVE;
-    int debug = flags & XCFLAGS_DEBUG;
-    int sent_last_iter, sent_this_iter, skip_this_iter;
+    u32 domid = ioctxt->domain;
+    int live = (ioctxt->flags & XCFLAGS_LIVE);
+    int debug = (ioctxt->flags & XCFLAGS_DEBUG);
+    int sent_last_iter, skip_this_iter;
     unsigned long dirtied_this_iter, faults_this_iter;
 
     /* Important tuning parameters */
@@ -246,7 +247,7 @@ int xc_linux_save(int xc_handle,
     unsigned long *live_shinfo;
 
     /* base of the region in which domain memory is mapped */
-    unsigned char *region_base;
+    unsigned char *region_base = NULL;
 
     /* A temporary mapping, and a copy, of the guest's suspend record. */
     suspend_record_t *p_srec;
@@ -266,16 +267,14 @@ int xc_linux_save(int xc_handle,
     int needed_to_fix = 0;
     int total_sent    = 0;
     
-    if ( mlock(&ctxt, sizeof(ctxt) ) )
-    {
-        PERROR("Unable to mlock ctxt");
+    if (mlock(&ctxt, sizeof(ctxt))) {
+        xcio_perror(ioctxt, "Unable to mlock ctxt");
         return 1;
     }
 
     /* Ensure that the domain exists, and that it is stopped. */
-    if ( xc_domain_pause(xc_handle, domid) )
-    {
-        PERROR("Could not pause domain");
+    if ( xc_domain_pause(xc_handle, domid) ){
+        xcio_perror(ioctxt, "Could not pause domain");
         goto out;
     }
 
@@ -283,9 +282,8 @@ int xc_linux_save(int xc_handle,
     shared_info_frame = op.u.getdomaininfo.shared_info_frame;
 
     /* A cheesy test to see whether the domain contains valid state. */
-    if ( ctxt.pt_base == 0 )
-    {
-        ERROR("Domain is not in a valid Linux guest OS state");
+    if ( ctxt.pt_base == 0 ){
+        xcio_error(ioctxt, "Domain is not in a valid Linux guest OS state");
         goto out;
     }
 
@@ -293,20 +291,17 @@ int xc_linux_save(int xc_handle,
        domid for this to succeed. */
     p_srec = mfn_mapper_map_single(xc_handle, domid,
                                    sizeof(*p_srec), PROT_READ, 
-                                   ctxt.cpu_ctxt.esi );
-
-    if (!p_srec)
-    {
-        ERROR("Couldn't map state record");
+                                   ctxt.cpu_ctxt.esi);
+    if (!p_srec){
+        xcio_error(ioctxt, "Couldn't map state record");
         goto out;
     }
 
     nr_pfns = p_srec->nr_pfns;
 
     /* cheesy sanity check */
-    if ( nr_pfns > 1024*1024 )
-    {
-        ERROR("Invalid state record -- pfn count out of range");
+    if ( nr_pfns > 1024*1024 ){
+        xcio_error(ioctxt, "Invalid state record -- pfn count out of range");
         goto out;
     }
 
@@ -316,9 +311,8 @@ int xc_linux_save(int xc_handle,
                               PAGE_SIZE, PROT_READ, 
                               p_srec->pfn_to_mfn_frame_list );
 
-    if (!live_pfn_to_mfn_frame_list)
-    {
-        ERROR("Couldn't map pfn_to_mfn_frame_list");
+    if (!live_pfn_to_mfn_frame_list){
+        xcio_error(ioctxt, "Couldn't map pfn_to_mfn_frame_list");
         goto out;
     }
 
@@ -345,24 +339,21 @@ int xc_linux_save(int xc_handle,
        (its not clear why it would want to change them, and we'll be OK
        from a safety POV anyhow. */
 
-    live_pfn_to_mfn_table = mfn_mapper_map_batch( xc_handle, domid, 
-                                                  PROT_READ,
-                                                  live_pfn_to_mfn_frame_list,
-                                                  (nr_pfns+1023)/1024 );  
-    if( !live_pfn_to_mfn_table )
-    {
-        PERROR("Couldn't map pfn_to_mfn table");
+    live_pfn_to_mfn_table = mfn_mapper_map_batch(xc_handle, domid, 
+                                                 PROT_READ,
+                                                 live_pfn_to_mfn_frame_list,
+                                                 (nr_pfns+1023)/1024 );  
+    if( !live_pfn_to_mfn_table ){
+        xcio_perror(ioctxt, "Couldn't map pfn_to_mfn table");
         goto out;
     }
 
 
     /* Canonicalise the pfn-to-mfn table frame-number list. */
     memcpy( pfn_to_mfn_frame_list, live_pfn_to_mfn_frame_list, PAGE_SIZE );
-    for ( i = 0; i < nr_pfns; i += 1024 )
-    {
-        if ( !translate_mfn_to_pfn(&pfn_to_mfn_frame_list[i/1024]) )
-        {
-            ERROR("Frame # in pfn-to-mfn frame list is not in pseudophys");
+    for ( i = 0; i < nr_pfns; i += 1024 ){
+        if ( !translate_mfn_to_pfn(&pfn_to_mfn_frame_list[i/1024]) ){
+            xcio_error(ioctxt, "Frame # in pfn-to-mfn frame list is not in pseudophys");
             goto out;
         }
     }
@@ -370,27 +361,24 @@ int xc_linux_save(int xc_handle,
     /* At this point, we can start the domain again if we're doing a
        live suspend */
 
-    if( live )
-    { 
+    if( live ){ 
         if ( xc_shadow_control( xc_handle, domid, 
                                 DOM0_SHADOW_CONTROL_OP_ENABLE_LOGDIRTY,
-                                NULL, 0, NULL, NULL ) < 0 )
-        {
-            ERROR("Couldn't enable shadow mode");
+                                NULL, 0, NULL, NULL ) < 0 ){
+            xcio_error(ioctxt, "Couldn't enable shadow mode");
             goto out;
         }
 
-        if ( xc_domain_unpause(xc_handle, domid) < 0 )
-        {
-            ERROR("Couldn't unpause domain");
+        if ( xc_domain_unpause(xc_handle, domid) < 0 ){
+            xcio_error(ioctxt, "Couldn't unpause domain");
             goto out;
         }
 
         last_iter = 0;
         sent_last_iter = 1<<20; /* 4GB's worth of pages */
-    }
-    else
+    } else{
         last_iter = 1;
+    }
 
 
     /* Setup to_send bitmap */
@@ -401,25 +389,22 @@ int xc_linux_save(int xc_handle,
         to_fix  = calloc( 1, sz );
         to_skip = malloc( sz );
 
-        if (!to_send || !to_fix || !to_skip)
-        {
-            ERROR("Couldn't allocate to_send array");
+        if (!to_send || !to_fix || !to_skip){
+            xcio_error(ioctxt, "Couldn't allocate to_send array");
             goto out;
         }
 
         memset( to_send, 0xff, sz );
 
-        if ( mlock( to_send, sz ) )
-        {
-            PERROR("Unable to mlock to_send");
+        if ( mlock( to_send, sz ) ){
+            xcio_perror(ioctxt, "Unable to mlock to_send");
             return 1;
         }
 
         /* (to fix is local only) */
 
-        if ( mlock( to_skip, sz ) )
-        {
-            PERROR("Unable to mlock to_skip");
+        if ( mlock( to_skip, sz ) ){
+            xcio_perror(ioctxt, "Unable to mlock to_skip");
             return 1;
         }
 
@@ -429,21 +414,19 @@ int xc_linux_save(int xc_handle,
        15->4 16->4 17->5 */
     for( i=nr_pfns-1, order_nr=0; i ; i>>=1, order_nr++ );
 
-    printf("nr_pfns=%d order_nr=%d\n",nr_pfns, order_nr);
+    printf("nr_pfns=%lu order_nr=%d\n",nr_pfns, order_nr);
 
     /* We want zeroed memory so use calloc rather than malloc. */
     pfn_type = calloc(BATCH_SIZE, sizeof(unsigned long));
     pfn_batch = calloc(BATCH_SIZE, sizeof(unsigned long));
 
-    if ( (pfn_type == NULL) || (pfn_batch == NULL) )
-    {
+    if ( (pfn_type == NULL) || (pfn_batch == NULL) ){
         errno = ENOMEM;
         goto out;
     }
 
-    if ( mlock( pfn_type, BATCH_SIZE * sizeof(unsigned long) ) )
-    {
-        ERROR("Unable to mlock");
+    if ( mlock( pfn_type, BATCH_SIZE * sizeof(unsigned long) ) ){
+        xcio_error(ioctxt, "Unable to mlock");
         goto out;
     }
 
@@ -452,8 +435,7 @@ int xc_linux_save(int xc_handle,
      * Quick belt and braces sanity check.
      */
 #if DEBUG
-    for ( i = 0; i < nr_pfns; i++ )
-    {
+    for ( i = 0; i < nr_pfns; i++ ){
         mfn = live_pfn_to_mfn_table[i];
 
         if( (live_mfn_to_pfn_table[mfn] != i) && (mfn != 0x80000004) )
@@ -467,29 +449,30 @@ int xc_linux_save(int xc_handle,
                                         PAGE_SIZE, PROT_READ,
                                         shared_info_frame);
 
-    if (!live_shinfo)
-    {
-        ERROR("Couldn't map live_shinfo");
+    if (!live_shinfo){
+        xcio_error(ioctxt, "Couldn't map live_shinfo");
         goto out;
     }
 
     /* Start writing out the saved-domain record. */
 
-    if ( (*writerfn)(writerst, "LinuxGuestRecord",    16) ||
-         (*writerfn)(writerst, name,                  sizeof(name)) ||
-         (*writerfn)(writerst, &nr_pfns,              sizeof(unsigned long)) ||
-         (*writerfn)(writerst, pfn_to_mfn_frame_list, PAGE_SIZE) )
-    {
-        ERROR("Error when writing to state file (1)");
+    if ( xcio_write(ioctxt, "LinuxGuestRecord",    16) ||
+         xcio_write(ioctxt, name,                  sizeof(name)) ||
+         xcio_write(ioctxt, &nr_pfns,              sizeof(unsigned long)) ||
+         xcio_write(ioctxt, pfn_to_mfn_frame_list, PAGE_SIZE) ){
+        xcio_error(ioctxt, "Error writing header");
+        goto out;
+    }
+    if(write_vmconfig(ioctxt)){
+        xcio_error(ioctxt, "Error writing vmconfig");
         goto out;
     }
 
-    track_cpu_usage( xc_handle, domid, 0, 0, 0, 0 );
+    track_cpu_usage(xc_handle, domid, 0, 0, 0, 0 );
 
     /* Now write out each data page, canonicalising page tables as we go... */
     
-    while(1)
-    {
+    while(1){
         unsigned int prev_pc, sent_this_iter, N, batch;
 
         iter++;
@@ -498,15 +481,13 @@ int xc_linux_save(int xc_handle,
         prev_pc = 0;
         N=0;
 
-        verbose_printf("Saving memory pages: iter %d   0%%", iter);
+        xcio_info(ioctxt, "Saving memory pages: iter %d   0%%", iter);
 
-        while( N < nr_pfns )
-        {
+        while( N < nr_pfns ){
             unsigned int this_pc = (N * 100) / nr_pfns;
 
-            if ( (this_pc - prev_pc) >= 5 )
-            {
-                verbose_printf("\b\b\b\b%3d%%", this_pc);
+            if ( (this_pc - prev_pc) >= 5 ){
+                xcio_info(ioctxt, "\b\b\b\b%3d%%", this_pc);
                 prev_pc = this_pc;
             }
 
@@ -516,9 +497,8 @@ int xc_linux_save(int xc_handle,
             if ( !last_iter && 
                  xc_shadow_control(xc_handle, domid, 
                                    DOM0_SHADOW_CONTROL_OP_PEEK,
-                                   to_skip, nr_pfns, NULL, NULL) != nr_pfns ) 
-            {
-                ERROR("Error peeking shadow bitmap");
+                                   to_skip, nr_pfns, NULL, NULL) != nr_pfns ){
+                xcio_error(ioctxt, "Error peeking shadow bitmap");
                 goto out;
             }
      
@@ -526,25 +506,26 @@ int xc_linux_save(int xc_handle,
             /* load pfn_type[] with the mfn of all the pages we're doing in
                this batch. */
 
-            for( batch = 0; batch < BATCH_SIZE && N < nr_pfns ; N++ )
-            {
+            for( batch = 0; batch < BATCH_SIZE && N < nr_pfns ; N++ ){
                 int n = permute(N, nr_pfns, order_nr );
 
                 if(0 && debug)
                     fprintf(stderr,"%d pfn= %08lx mfn= %08lx %d   "
                             "[mfn]= %08lx\n",
-                            iter, n, live_pfn_to_mfn_table[n],
+                            iter, (unsigned long)n, live_pfn_to_mfn_table[n],
                             test_bit(n,to_send),
                             live_mfn_to_pfn_table[
                                 live_pfn_to_mfn_table[n]&0xFFFFF]);
 
-                if (!last_iter && test_bit(n, to_send) && test_bit(n, to_skip))
+                if (!last_iter && test_bit(n, to_send) && test_bit(n, to_skip)){
                     skip_this_iter++; /* stats keeping */
+                }
 
                 if (! ( (test_bit(n, to_send) && !test_bit(n, to_skip)) ||
                         (test_bit(n, to_send) && last_iter) ||
-                        (test_bit(n, to_fix)  && last_iter) )   )
+                        (test_bit(n, to_fix)  && last_iter) )   ){
                     continue;
+                }
 
                 /* we get here if:
                    1. page is marked to_send & hasn't already been re-dirtied
@@ -555,8 +536,7 @@ int xc_linux_save(int xc_handle,
                 pfn_batch[batch] = n;
                 pfn_type[batch] = live_pfn_to_mfn_table[n];
 
-                if( pfn_type[batch] == 0x80000004 )
-                {
+                if( pfn_type[batch] == 0x80000004 ){
                     /* not currently in pusedo-physical map -- set bit
                        in to_fix that we must send this page in last_iter
                        unless its sent sooner anyhow */
@@ -570,8 +550,7 @@ int xc_linux_save(int xc_handle,
                 }
 
                 if ( last_iter && test_bit(n, to_fix) && 
-                     !test_bit(n, to_send) )
-                {
+                     !test_bit(n, to_send) ){
                     needed_to_fix++;
                     DPRINTF("Fix! iter %d, pfn %lx. mfn %lx\n",
                             iter,n,pfn_type[batch]);
@@ -584,33 +563,30 @@ int xc_linux_save(int xc_handle,
      
             DDPRINTF("batch %d:%d (n=%d)\n",iter,batch,n);
 
-            if ( batch == 0 ) 
+            if ( batch == 0 ){
                 goto skip; /* very unlikely */
+            }
       
             if ( (region_base = mfn_mapper_map_batch(xc_handle, domid, 
                                                      PROT_READ,
                                                      pfn_type,
-                                                     batch)) == 0 )
-            {
-                PERROR("map batch failed");
+                                                     batch)) == 0 ){
+                xcio_perror(ioctxt, "map batch failed");
                 goto out;
             }
      
-            if ( get_pfn_type_batch(xc_handle, domid, batch, pfn_type) )
-            {
-                ERROR("get_pfn_type_batch failed");
+            if ( get_pfn_type_batch(xc_handle, domid, batch, pfn_type) ){
+                xcio_error(ioctxt, "get_pfn_type_batch failed");
                 goto out;
             }
      
-            for ( j = 0; j < batch; j++ )
-            {
-                if ( (pfn_type[j] & LTAB_MASK) == XTAB )
-                {
+            for ( j = 0; j < batch; j++ ){
+                if ( (pfn_type[j] & LTAB_MASK) == XTAB ){
                     DDPRINTF("type fail: page %i mfn %08lx\n",j,pfn_type[j]);
                     continue;
                 }
   
-                if ( 0 && debug )
+                if ( 0 && debug ){
                     fprintf(stderr,"%d pfn= %08lx mfn= %08lx "
                             "[mfn]= %08lx sum= %08lx\n",
                             iter, 
@@ -619,6 +595,7 @@ int xc_linux_save(int xc_handle,
                             live_mfn_to_pfn_table[pfn_type[j]&(~LTAB_MASK)],
                             csum_page(region_base + (PAGE_SIZE*j))
                         );
+                }
 
                 /* canonicalise mfn->pfn */
                 pfn_type[j] = (pfn_type[j] & LTAB_MASK) |
@@ -626,32 +603,27 @@ int xc_linux_save(int xc_handle,
             }
 
      
-            if ( (*writerfn)(writerst, &batch, sizeof(int) ) )
-            {
-                ERROR("Error when writing to state file (2)");
+            if ( xcio_write(ioctxt, &batch, sizeof(int) ) ){
+                xcio_error(ioctxt, "Error when writing to state file (2)");
                 goto out;
             }
 
-            if ( (*writerfn)(writerst, pfn_type, sizeof(unsigned long)*j ) )
-            {
-                ERROR("Error when writing to state file (3)");
+            if ( xcio_write(ioctxt, pfn_type, sizeof(unsigned long)*j ) ){
+                xcio_error(ioctxt, "Error when writing to state file (3)");
                 goto out;
             }
      
             /* entering this loop, pfn_type is now in pfns (Not mfns) */
-            for( j = 0; j < batch; j++ )
-            {
+            for( j = 0; j < batch; j++ ){
                 /* write out pages in batch */
   
-                if( (pfn_type[j] & LTAB_MASK) == XTAB)
-                {
+                if( (pfn_type[j] & LTAB_MASK) == XTAB){
                     DDPRINTF("SKIP BOGUS page %i mfn %08lx\n",j,pfn_type[j]);
                     continue;
                 }
   
                 if ( ((pfn_type[j] & LTAB_MASK) == L1TAB) || 
-                     ((pfn_type[j] & LTAB_MASK) == L2TAB) )
-                {
+                     ((pfn_type[j] & LTAB_MASK) == L2TAB) ){
       
                     memcpy(page, region_base + (PAGE_SIZE*j), PAGE_SIZE);
       
@@ -659,8 +631,7 @@ int xc_linux_save(int xc_handle,
                           k < (((pfn_type[j] & LTAB_MASK) == L2TAB) ? 
                                (HYPERVISOR_VIRT_START >> L2_PAGETABLE_SHIFT) : 
                                1024); 
-                          k++ )
-                    {
+                          k++ ){
                         unsigned long pfn;
 
                         if ( !(page[k] & _PAGE_PRESENT) ) continue;
@@ -683,20 +654,16 @@ int xc_linux_save(int xc_handle,
                         page[k] |= pfn << PAGE_SHIFT;
                     } /* end of page table rewrite for loop */
       
-                    if ( (*writerfn)(writerst, page, PAGE_SIZE) )
-                    {
-                        ERROR("Error when writing to state file (4)");
+                    if ( xcio_write(ioctxt, page, PAGE_SIZE) ){
+                        xcio_error(ioctxt, "Error when writing to state file (4)");
                         goto out;
                     }
       
-                }  /* end of it's a PT page */
-                else
-                {  /* normal page */
-
-                    if ( (*writerfn)(writerst, region_base + (PAGE_SIZE*j), 
-                                     PAGE_SIZE) )
-                    {
-                        ERROR("Error when writing to state file (5)");
+                }  /* end of it's a PT page */ else {  /* normal page */
+
+                    if ( xcio_write(ioctxt, region_base + (PAGE_SIZE*j), 
+                                     PAGE_SIZE) ){
+                        xcio_error(ioctxt, "Error when writing to state file (5)");
                         goto out;
                     }
                 }
@@ -712,40 +679,35 @@ int xc_linux_save(int xc_handle,
 
         total_sent += sent_this_iter;
 
-        verbose_printf("\r %d: sent %d, skipped %d, ", 
+        xcio_info(ioctxt, "\r %d: sent %d, skipped %d, ", 
                        iter, sent_this_iter, skip_this_iter );
 
-        if ( last_iter )
-        {
+        if ( last_iter ){
             track_cpu_usage( xc_handle, domid, 0, sent_this_iter, 0, 1);
-
-            verbose_printf("Total pages sent= %d (%.2fx)\n", 
+            xcio_info(ioctxt, "Total pages sent= %d (%.2fx)\n", 
                            total_sent, ((float)total_sent)/nr_pfns );
-            verbose_printf("(of which %d were fixups)\n", needed_to_fix  );
+            xcio_info(ioctxt, "(of which %d were fixups)\n", needed_to_fix  );
         }       
 
-        if ( debug && last_iter )
-        {
+        if (last_iter && debug){
             int minusone = -1;
             memset( to_send, 0xff, nr_pfns/8 );
             debug = 0;
             printf("Entering debug resend-all mode\n");
     
             /* send "-1" to put receiver into debug mode */
-            if ( (*writerfn)(writerst, &minusone, sizeof(int)) )
+            if ( xcio_write(ioctxt, &minusone, sizeof(int)) )
             {
-                ERROR("Error when writing to state file (6)");
+                xcio_error(ioctxt, "Error when writing to state file (6)");
                 goto out;
             }
 
             continue;
         }
 
-        if ( last_iter )
-            break;
+        if ( last_iter ) break;
 
-        if ( live )
-        {
+        if ( live ) {
             if ( (iter >= max_iters) || 
                  (sent_this_iter+skip_this_iter < 50) || 
                  (total_sent > nr_pfns*max_factor) )
@@ -761,7 +723,7 @@ int xc_linux_save(int xc_handle,
                                     to_send, nr_pfns, &faults_this_iter,
                                     &dirtied_this_iter) != nr_pfns ) 
             {
-                ERROR("Error flushing shadow PT");
+                xcio_error(ioctxt, "Error flushing shadow PT");
                 goto out;
             }
 
@@ -781,9 +743,9 @@ int xc_linux_save(int xc_handle,
     rc = 0;
     
     /* Zero terminate */
-    if ( (*writerfn)(writerst, &rc, sizeof(int)) )
+    if ( xcio_write(ioctxt, &rc, sizeof(int)) )
     {
-        ERROR("Error when writing to state file (6)");
+        xcio_error(ioctxt, "Error when writing to state file (6)");
         goto out;
     }
 
@@ -794,50 +756,42 @@ int xc_linux_save(int xc_handle,
     if ( (do_dom0_op(xc_handle, &op) < 0) || 
          ((u32)op.u.getdomaininfo.domain != domid) )
     {
-        PERROR("Could not get info on domain");
+        xcio_perror(ioctxt, "Could not get info on domain");
         goto out;
     }
 
     /* Canonicalise the suspend-record frame number. */
-    if ( !translate_mfn_to_pfn(&ctxt.cpu_ctxt.esi) )
-    {
-        ERROR("State record is not in range of pseudophys map");
+    if ( !translate_mfn_to_pfn(&ctxt.cpu_ctxt.esi) ){
+        xcio_error(ioctxt, "State record is not in range of pseudophys map");
         goto out;
     }
 
     /* Canonicalise each GDT frame number. */
-    for ( i = 0; i < ctxt.gdt_ents; i += 512 )
-    {
-        if ( !translate_mfn_to_pfn(&ctxt.gdt_frames[i]) )
-        {
-            ERROR("GDT frame is not in range of pseudophys map");
+    for ( i = 0; i < ctxt.gdt_ents; i += 512 ) {
+        if ( !translate_mfn_to_pfn(&ctxt.gdt_frames[i]) ) {
+            xcio_error(ioctxt, "GDT frame is not in range of pseudophys map");
             goto out;
         }
     }
 
     /* Canonicalise the page table base pointer. */
-    if ( !MFN_IS_IN_PSEUDOPHYS_MAP(ctxt.pt_base >> PAGE_SHIFT) )
-    {
-        ERROR("PT base is not in range of pseudophys map");
+    if ( !MFN_IS_IN_PSEUDOPHYS_MAP(ctxt.pt_base >> PAGE_SHIFT) ) {
+        xcio_error(ioctxt, "PT base is not in range of pseudophys map");
         goto out;
     }
     ctxt.pt_base = live_mfn_to_pfn_table[ctxt.pt_base >> PAGE_SHIFT] << 
         PAGE_SHIFT;
 
-    if ( (*writerfn)(writerst, &ctxt,       sizeof(ctxt)) ||
-         (*writerfn)(writerst, live_shinfo, PAGE_SIZE) )
-    {
-        ERROR("Error when writing to state file (1)");
+    if ( xcio_write(ioctxt, &ctxt,       sizeof(ctxt)) ||
+         xcio_write(ioctxt, live_shinfo, PAGE_SIZE) ) {
+        xcio_error(ioctxt, "Error when writing to state file (1)");
         goto out;
     }
     munmap(live_shinfo, PAGE_SIZE);
 
  out:
-    if ( pfn_type != NULL )
-        free(pfn_type);
-
+    if ( pfn_type != NULL ) free(pfn_type);
     DPRINTF("Save exit rc=%d\n",rc);
-    
     return !!rc;
 
 }
index 81213cff2178d2f836604bc407e802a5c4ed5638..2b70657e498ef774033588fc68ab64fd59faf516 100644 (file)
@@ -136,6 +136,8 @@ int close_pfn_mapper(int pm_handle);
 void *map_pfn_writeable(int pm_handle, unsigned long pfn);
 void *map_pfn_readonly(int pm_handle, unsigned long pfn);
 void unmap_pfn(int pm_handle, void *vaddr);
+int get_pfn_type_batch(int xc_handle, u32 dom, int num, unsigned long *arr);
+unsigned long csum_page (void * page);
 
 /*
  * MMU updates.
@@ -202,4 +204,5 @@ void * mfn_mapper_queue_entry(mfn_mapper_t *t, int offset,
 
 long long  xc_domain_get_cpu_usage( int xc_handle, domid_t domid );
 
+#include "xc_io.h"
 #endif /* __XC_PRIVATE_H__ */
index b52114aab256ab391b17c4d95fa5368281d87a89..f82cd0e98f010da4cf1c3ef39b65dcbe19642d3f 100644 (file)
@@ -14,6 +14,8 @@
 #include <sys/socket.h>
 #include <netdb.h>
 #include <arpa/inet.h>
+#include "xc_private.h"
+#include "gzip_stream.h"
 
 /* Needed for Python versions earlier than 2.3. */
 #ifndef PyMODINIT_FUNC
@@ -189,115 +191,31 @@ static PyObject *pyxc_domain_getinfo(PyObject *self,
     return list;
 }
 
-static PyObject *tcp_save(XcObject *xc, u32 dom, char *url, unsigned flags){
-#define max_namelen 64
-    char server[max_namelen];
-    char *port_s;
-    int port=777;
-    int sd = -1;
-    struct hostent *h;
-    struct sockaddr_in s;
-    int sockbufsize;
+static int file_save(XcObject *xc, XcIOContext *ctxt, char *state_file){
     int rc = -1;
-    
-    int writerfn(void *fd, const void *buf, size_t count) {
-        int tot = 0, rc;
-        do {
-            rc = write( (int) fd, ((char*)buf)+tot, count-tot );
-            if ( rc < 0 ) { perror("WRITE"); return rc; };
-            tot += rc;
-        }
-        while ( tot < count );
-        return 0;
-    }
-    
-    strncpy( server, url+strlen("tcp://"), max_namelen);
-    server[max_namelen-1]='\0';
-    if ( (port_s = strchr(server,':')) != NULL ) {
-        *port_s = '\0';
-        port = atoi(port_s+1);
+    int fd = -1;
+    int open_flags = (O_CREAT | O_EXCL | O_WRONLY);
+    int open_mode = 0644;
+
+    fd = open(state_file, open_flags, open_mode);
+    if(fd < 0){
+        xcio_perror(ctxt, "Could not open file for writing");
+        goto exit;
     }
-    printf("X server=%s port=%d\n",server,port);
-    h = gethostbyname(server);
-    sd = socket(AF_INET, SOCK_STREAM,0);
-    if (sd < 0) goto serr;
-    s.sin_family = AF_INET;
-    bcopy ( h->h_addr, &(s.sin_addr.s_addr), h->h_length);
-    s.sin_port = htons(port);
-    if ( connect(sd, (struct sockaddr *) &s, sizeof(s)) ) goto serr;
-    sockbufsize=128*1024;
-    if ( setsockopt(sd, SOL_SOCKET, SO_SNDBUF, &sockbufsize, sizeof sockbufsize) < 0 ) goto serr;
-
-    if ( xc_linux_save(xc->xc_handle, dom, flags, writerfn, (void*)sd) == 0 ) {
-        if ( read( sd, &rc, sizeof(int) ) != sizeof(int) ) goto serr;
-  
-        if ( rc == 0 ) {
-                printf("Migration succesful -- destroy local copy\n");
-                xc_domain_destroy(xc->xc_handle, dom);
-                close(sd);
-                Py_INCREF(zero);
-                return zero;
-        } else {
-            errno = rc;
-        }
+    /* Compression rate 1: we want speed over compression. 
+     * We're mainly going for those zero pages, after all.
+     */
+    ctxt->io = gzip_stream_fdopen(fd, "wb1");
+    if(!ctxt->io){
+        xcio_perror(ctxt, "Could not allocate compression state");
+        goto exit;
     }
-
-  serr:
-    printf("Migration failed -- restart local copy\n");
-    xc_domain_unpause(xc->xc_handle, dom);
-    PyErr_SetFromErrno(xc_error);
-    if ( sd >= 0 ) close(sd);
-    return NULL;
-
-}
-
-static PyObject *file_save(XcObject *xc, u32 dom, char *state_file, unsigned flags){
-        int fd = -1;
-        gzFile gfd = NULL;
-
-        int writerfn(void *fd, const void *buf, size_t count) {
-            int rc;
-            while ( ((rc = gzwrite( (gzFile*)fd, (void*)buf, count)) == -1) && 
-                    (errno = EINTR) )
-                continue;
-            return ! (rc == count);
-        }
-
-        if (strncmp(state_file,"file:",strlen("file:")) == 0){
-            state_file += strlen("file:");
-        }
-
-        if ( (fd = open(state_file, O_CREAT|O_EXCL|O_WRONLY, 0644)) == -1 ) {
-            perror("Could not open file for writing");
-            goto err;
-        }
-        /*
-         * Compression rate 1: we want speed over compression. 
-         * We're mainly going for those zero pages, after all.
-         */
-        if ( (gfd = gzdopen(fd, "wb1")) == NULL ) {
-            perror("Could not allocate compression state for state file");
-            close(fd);
-            goto err;
-        }
-        if ( xc_linux_save(xc->xc_handle, dom, flags, writerfn, gfd) == 0 ) {
-            /* kill domain. We don't want to do this for checkpointing, but
-               if we don't do it here I think people will hurt themselves
-               by accident... */
-            xc_domain_destroy(xc->xc_handle, dom);
-            gzclose(gfd);
-            close(fd);
-
-            Py_INCREF(zero);
-            return zero;
-        }
-
-    err:
-        PyErr_SetFromErrno(xc_error);
-        if ( gfd != NULL ) gzclose(gfd);
-        if ( fd >= 0 ) close(fd);
-        unlink(state_file);
-        return NULL;
+    rc = xc_linux_save(xc->xc_handle, ctxt);
+  exit:
+    if(ctxt->io) IOStream_close(ctxt->io);
+    if(fd >= 0) close(fd);
+    unlink(state_file);
+    return rc;
 }
 
 static PyObject *pyxc_linux_save(PyObject *self,
@@ -306,33 +224,54 @@ static PyObject *pyxc_linux_save(PyObject *self,
 {
     XcObject *xc = (XcObject *)self;
 
-    u32   dom;
+    u32 dom;
     char *state_file;
-    int   progress = 1, live = -1, debug = 0;
+    int progress = 1, debug = 0;
     unsigned int flags = 0;
     PyObject *val = NULL;
+    int rc = -1;
+    XcIOContext ioctxt = { .info = iostdout, .err = iostderr };
 
-    static char *kwd_list[] = { "dom", "state_file", "progress", 
-                                "live", "debug", NULL };
+    static char *kwd_list[] = { "dom", "state_file", "vmconfig", "progress", "debug", NULL };
 
-    if ( !PyArg_ParseTupleAndKeywords(args, kwds, "is|iii", kwd_list, 
-                                      &dom, &state_file, &progress, 
-                                      &live, &debug) )
+    if (!PyArg_ParseTupleAndKeywords(args, kwds, "is|siii", kwd_list, 
+                                     &ioctxt.domain,
+                                     &state_file,
+                                     &ioctxt.vmconfig,
+                                     &progress, 
+                                     &debug)){
+        goto exit;
+    }
+    ioctxt.vmconfig_n = (ioctxt.vmconfig ? strlen(ioctxt.vmconfig) : 0);
+    if (progress)  ioctxt.flags |= XCFLAGS_VERBOSE;
+    if (debug)     ioctxt.flags |= XCFLAGS_DEBUG;
+    if(!state_file || state_file[0] == '\0') goto exit;
+    rc = file_save(xc, &ioctxt, state_file);
+    if(rc){
+        PyErr_SetFromErrno(xc_error);
         goto exit;
+    } 
+    //xc_domain_destroy(xc->xc_handle, dom);
+    Py_INCREF(zero);
+    val = zero;
+  exit:
+    return val;
+}
 
-    if (progress)  flags |= XCFLAGS_VERBOSE;
-    if (live == 1) flags |= XCFLAGS_LIVE;
-    if (debug)     flags |= XCFLAGS_DEBUG;
 
-    if ( strncmp(state_file,"tcp:", strlen("tcp:")) == 0 ) {
-        /* default to live for tcp */
-        if (live == -1) flags |= XCFLAGS_LIVE;
-        val = tcp_save(xc, dom, state_file, flags);
-    } else {
-        val = file_save(xc, dom, state_file, flags);
+static int file_restore(XcObject *xc, XcIOContext *ioctxt, char *state_file){
+    int rc = -1;
+
+    ioctxt->io = gzip_stream_fopen(state_file, "rb");
+    if (!ioctxt->io) {
+        xcio_perror(ioctxt, "Could not open file for reading");
+        goto exit;
     }
+
+    rc = xc_linux_restore(xc->xc_handle, ioctxt);
   exit:
-    return val;
+    if(ioctxt->io) IOStream_close(ioctxt->io);
+    return rc;
 }
 
 static PyObject *pyxc_linux_restore(PyObject *self,
@@ -340,161 +279,37 @@ static PyObject *pyxc_linux_restore(PyObject *self,
                                     PyObject *kwds)
 {
     XcObject *xc = (XcObject *)self;
+    char *state_file;
+    int progress = 1, debug = 0;
+    u32 dom;
+    PyObject *val = NULL;
+    XcIOContext ioctxt = { .info = iostdout, .err = iostderr };
+    int rc =-1;
 
-    char        *state_file;
-    int          progress = 1;
-    u32          dom;
-    unsigned int flags = 0;
-
-    static char *kwd_list[] = { "dom", "state_file", "progress", NULL };
-
-    if ( !PyArg_ParseTupleAndKeywords(args, kwds, "is|i", kwd_list, 
-                                      &dom, &state_file, &progress) )
-        return NULL;
-
-    if ( progress )
-        flags |= XCFLAGS_VERBOSE;
-
-    if ( strncmp(state_file,"tcp:", strlen("tcp:")) == 0 )
-    {
-#define max_namelen 64
-        char server[max_namelen];
-        char *port_s;
-        int port=777;
-        int ld = -1, sd = -1;
-        struct hostent *h;
-        struct sockaddr_in s, d, p;
-        socklen_t dlen, plen;
-        int sockbufsize;
-        int on = 1, rc = -1;
-
-        int readerfn(void *fd, void *buf, size_t count)
-        {
-            int rc, tot = 0;
-            do { 
-                rc = read( (int) fd, ((char*)buf)+tot, count-tot ); 
-                if ( rc < 0 ) { perror("READ"); return rc; }
-                if ( rc == 0 ) { printf("read: need %d, tot=%d got zero\n",
-                                        count-tot, tot); return -1; }
-                tot += rc;
-            } 
-            while ( tot < count );
-            return 0;
-        }
-
-        strncpy( server, state_file+strlen("tcp://"), max_namelen);
-        server[max_namelen-1]='\0';
-        if ( (port_s = strchr(server,':')) != NULL )
-        {
-            *port_s = '\0';
-            port = atoi(port_s+1);
-        }
-
-        printf("X server=%s port=%d\n",server,port);
-        h = gethostbyname(server);
-        ld = socket (AF_INET,SOCK_STREAM,0);
-        if ( ld < 0 ) goto serr;
-        s.sin_family = AF_INET;
-        s.sin_addr.s_addr = htonl(INADDR_ANY);
-        s.sin_port = htons(port);
-
-        if ( setsockopt(ld, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) < 0 )
-            goto serr;
-
-        if ( bind(ld, (struct sockaddr *) &s, sizeof(s)) ) 
-            goto serr;
-
-        if ( listen(ld, 1) )
-            goto serr;
-
-        dlen=sizeof(struct sockaddr);
-        if ( (sd = accept(ld, (struct sockaddr *) &d, &dlen )) < 0 )
-            goto serr;
-
-        plen = sizeof(p);
-        if ( getpeername(sd, (struct sockaddr_in *) &p, 
-                         &plen) < 0 )
-            goto serr;
-
-        printf("Accepted connection from %s\n", inet_ntoa(p.sin_addr));
-        sockbufsize=128*1024;
-        if ( setsockopt(sd, SOL_SOCKET, SO_SNDBUF, &sockbufsize, 
-                        sizeof sockbufsize) < 0 ) 
-            goto serr;
-
-        rc = xc_linux_restore(xc->xc_handle, dom, flags, 
-                              readerfn, (void*)sd, &dom);
-
-        write( sd, &rc, sizeof(int) ); 
+    static char *kwd_list[] = { "state_file", "progress", "debug", NULL };
 
-        if (rc == 0)
-        {
-            close(sd);
-            Py_INCREF(zero);
-            return zero;
-        }
-        errno = rc;
+    if (!PyArg_ParseTupleAndKeywords(args, kwds, "is|ii", kwd_list, 
+                                     &ioctxt.domain,
+                                     &state_file,
+                                     &progress,
+                                     &debug)){
+        goto exit;
+    }
+    if (progress) ioctxt.flags |= XCFLAGS_VERBOSE;
+    if (debug)    ioctxt.flags |= XCFLAGS_DEBUG;
 
-    serr:
-        PyErr_SetFromErrno(xc_error);
-        if ( ld >= 0 ) close(ld);
-        if ( sd >= 0 ) close(sd);
-        return NULL;
-    }    
-    else
-    {
-        int fd = -1;
-        gzFile gfd = NULL;
-
-        int readerfn(void *fd, void *buf, size_t count)
-        {
-            int rc;
-            while ( ((rc = gzread( (gzFile*)fd, (void*)buf, count)) == -1) && 
-                    (errno = EINTR) )
-                continue;
-            return ! (rc == count);
-        }
-
-        if ( strncmp(state_file,"file:",strlen("file:")) == 0 )
-            state_file += strlen("file:");
-
-        if ( (fd = open(state_file, O_RDONLY)) == -1 )
-        {
-            perror("Could not open file for writing");
-            goto err;
-        }
-
-        /*
-         * Compression rate 1: we want speed over compression. 
-         * We're mainly going for those zero pages, after all.
-         */
-        if ( (gfd = gzdopen(fd, "rb")) == NULL )
-        {
-            perror("Could not allocate compression state for state file");
-            close(fd);
-            goto err;
-        }
-
-
-        if ( xc_linux_restore(xc->xc_handle, dom, flags, 
-                              readerfn, gfd, &dom) == 0 )
-        {
-            gzclose(gfd);
-            close(fd);
-
-            Py_INCREF(zero);
-            return zero;
-        }
-
-    err:
+    if(!state_file || state_file[0] == '\0') goto exit;
+    rc = file_restore(xc, &ioctxt, state_file);
+    if(rc){
         PyErr_SetFromErrno(xc_error);
-        if ( gfd != NULL ) gzclose(gfd);
-        if ( fd >= 0 ) close(fd);
-        return NULL;
+        goto exit;
     }
-
+    val = Py_BuildValue("{s:i,s:s}",
+                        "dom", ioctxt.domain,
+                        "vmconfig", ioctxt.vmconfig);
+    //? free(ioctxt.vmconfig);
+  exit:
+    return val;
 }
 
 static PyObject *pyxc_linux_build(PyObject *self,
index b295ed295b2c7cd81a01bbf79ce6f3871ca5a1e6..8efe5ca1a06c703c054722a2cc9635fe2f60569e 100644 (file)
@@ -3,12 +3,17 @@ from distutils.core import setup, Extension
 
 module = Extension("xc",
                    extra_compile_args   = ["-fno-strict-aliasing"],
-                   include_dirs         = ["../lib"],
-                   library_dirs         = ["../lib"],
+                   include_dirs         = ["../lib",
+                                           "../../../xen/include/hypervisor-ifs",
+                                           "../../../linux-xen-sparse/include",
+                                           "../../xu/lib",
+                                           "../../lib" ],
+                   library_dirs         = ["../lib",
+                                           "../../lib" ],
                    libraries            = ["xc"],
                    sources              = ["Xc.c"])
 
 setup(name = "xc",
-      version = "1.0",
+      version = "2.0",
       ext_package = "xen.ext",
       ext_modules = [module])
index 446b7f7fae3f8a08fe3ef66ab5bfbf94a170dbd9..d1e79a2b7ddba57947b0ac70466b7fb43baf8c0b 100644 (file)
@@ -235,7 +235,7 @@ class XendDomain:
     def domain_get(self, id):
         id = str(id)
         self.refresh_domain(id)
-        return self.domain[id]
+        return self.domain.get(id)
     
     def domain_unpause(self, id):
         """(Re)start domain running.
@@ -278,22 +278,26 @@ class XendDomain:
         """
         # Need a cancel too?
         pass
-    
+
     def domain_save(self, id, dst, progress=0):
         """Save domain state to file, destroy domain.
         """
         dom = int(id)
+        dominfo = self.domain_get(id)
+        if not dominfo:
+            return -1
+        vmconfig = sxp.to_string(dominfo.sxpr())
         self.domain_pause(id)
         eserver.inject('xend.domain.save', id)
-        rc = xc.linux_save(dom=dom, state_file=dst, progress=progress)
+        rc = xc.linux_save(dom=dom, state_file=dst, vmconfig=vmconfig, progress=progress)
         if rc == 0:
             self.domain_destroy(id)
         return rc
     
-    def domain_restore(self, src, config, progress=0):
+    def domain_restore(self, src, progress=0):
         """Restore domain from file.
         """
-        dominfo = XendDomainInfo.dom_restore(dom, config)
+        dominfo = XendDomainInfo.vm_restore(src, progress=progress)
         self._add_domain(dominfo.id, dominfo)
         return dominfo
     
index 964285ec1320570a949d2267b483fa9850c93ad9..05c7ba26b28832866b7e0f5ea6ec7b9f825abc4a 100644 (file)
@@ -272,22 +272,23 @@ def vm_recreate(config, info):
         d.callback(vm)
     return d
 
-def vm_restore(src, config, progress=0):
+def vm_restore(src, progress=0):
     """Restore a VM from a disk image.
 
     src      saved state to restore
-    config   configuration
     progress progress reporting flag
     returns  deferred
     raises   VmError for invalid configuration
     """
     vm = XendDomainInfo()
-    vm.config = config
-    ostype = "linux" #todo set from config
+    ostype = "linux" #todo Set from somewhere (store in the src?).
     restorefn = getattr(xc, "%s_restore" % ostype)
-    dom = restorefn(state_file=src, progress=progress)
+    d = restorefn(state_file=src, progress=progress)
+    dom = int(d['dom'])
     if dom < 0:
         raise VMError('restore failed')
+    vmconfig = sxp.from_string(d['vmconfig'])
+    vm.config = sxp.child_value(vmconfig, 'config')
     deferred = vm.dom_configure(dom)
     def vifs_cb(val, vm):
         vif_up(vm.ipaddrs)
@@ -855,9 +856,7 @@ def vm_field_vfr(vm, config, val, index):
         if not ip:
             raise VmError('vfr: missing ip address')
         ipaddrs.append(ip);
-        #Don't do this in new i/o model.
-        #print 'vm_field_vfr> add rule', 'dom=', vm.dom, 'vif=', vif, 'ip=', ip
-        #xenctl.ip.setup_vfr_rules_for_vif(vm.dom, vif, ip)
+        # todo: Configure the ipaddrs.
     vm.ipaddrs = ipaddrs
 
 def vnet_bridge(vnet, vmac, dom, idx):
index c3684f242bb0805cbb278e6b29586aaabfe8dea5..7b9d3563a8146a9e441c57618164a720acfad2f9 100644 (file)
@@ -44,13 +44,6 @@ class SrvDomain(SrvDir):
         val = fn(req.args, {'dom': self.dom.id})
         return val
 
-    def op_restore(self, op, req):
-        fn = FormFn(self.xd.domain_restore,
-                    [['dom', 'int'],
-                     ['file', 'str']])
-        val = fn(req.args, {'dom': self.dom.id})
-        return val
-        
     def op_migrate(self, op, req):
         fn = FormFn(self.xd.domain_migrate,
                     [['dom', 'int'],
index cb5d8e38cd8b3939ef27a5a548d675b290b9adcc..af4bc7a15c298b3dc1e5c2110196b6eccf623550 100644 (file)
@@ -7,6 +7,7 @@ from twisted.web import error
 
 from xen.xend import sxp
 from xen.xend import XendDomain
+from xen.xend.Args import FormFn
 
 from SrvDir import SrvDir
 from SrvDomain import SrvDomain
@@ -88,6 +89,12 @@ class SrvDomainDir(SrvDir):
             out.close()
             return val
 
+    def op_restore(self, op, req):
+        fn = FormFn(self.xd.domain_restore,
+                    [['file', 'str']])
+        val = fn(req.args)
+        return val
+        
     def render_POST(self, req):
         return self.perform(req)
 
@@ -129,3 +136,9 @@ class SrvDomainDir(SrvDir):
         req.write('<button type="submit" name="op" value="create">Create Domain</button>')
         req.write('Config <input type="file" name="config"><br>')
         req.write('</form>')
+        req.write('<form method="post" action="%s" enctype="multipart/form-data">'
+                  % req.prePathURL())
+        req.write('<button type="submit" name="op" value="create">Restore Domain</button>')
+        req.write('State <input type="string" name="state"><br>')
+        req.write('</form>')
+        
index dd4fece6f01e0ac7c6f203e4b11f94f8072b49e0..a08a0b6c0f96e48dc723944da1396f29878eed81 100644 (file)
@@ -16,6 +16,7 @@ import sys
 import types
 import errno
 import string
+from StringIO import StringIO
 
 __all__ = [
     "mime_type", 
@@ -521,6 +522,28 @@ def elements(sxpr, ctxt=None):
                 yield v
         i += 1
 
+def to_string(sxpr):
+    """Convert an sxpr to a string.
+
+    sxpr sxpr
+    returns string
+    """
+    io = StringIO()
+    sxp.show(sxpr, io)
+    io.seek(0)
+    val = io.getvalue()
+    io.close()
+    return val
+
+def from_string(str):
+    """Create an sxpr by parsing a string.
+
+    str string
+    returns sxpr
+    """
+    io = StringIO(str)
+    return parse(io)
+
 def parse(io):
     """Completely parse all input from 'io'.
 
index 65cfe8d820f949572eaef83cc90a27dc7fae9c89..77dcb1e75ac9ed582dc8c922ee6083744f191192 100644 (file)
@@ -13,8 +13,8 @@
 
    xend stop
 
-   Unfortunately restarting it upsets the channel to dom0 and
-   domain management stops working - needs a reboot to fix.
+   The daemon should reconnect to device control interfaces
+   and recover its state when restarted.
 """
 import os
 import sys